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 {
"retval", endOfRetVal},
681 {
"rtfinclude", endOfLine },
682 {
"section", endOfLabel },
683 {
"skip", endOfLine },
684 {
"skipline", endOfLine },
685 {
"snippet", endOfLine },
686 {
"snippetdoc", endOfLine },
687 {
"snippetlineno", endOfLine },
688 {
"struct", endOfLine },
689 {
"subpage", endOfLabel },
690 {
"subparagraph", endOfLabel },
691 {
"subsubparagraph",endOfLabel },
692 {
"subsection", endOfLabel },
693 {
"subsubsection", endOfLabel },
694 {
"throw", endOfLabel },
695 {
"throws", endOfLabel },
696 {
"tparam", endOfLabel },
697 {
"typedef", endOfLine },
698 {
"plantumlfile", endOfLine },
699 {
"union", endOfLine },
700 {
"until", endOfLine },
701 {
"var", endOfLine },
702 {
"verbinclude", endOfLine },
703 {
"weakgroup", endOfLabel },
704 {
"xmlinclude", endOfLine },
705 {
"xrefitem", endOfLabel }
708 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
709 if (isEscaped)
return 0;
711 const size_t size = data.size();
713 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
714 if (
end==1)
return 0;
715 std::string cmdName(data.substr(1,
end-1));
717 auto it = cmdNames.find(cmdName);
718 if (it!=cmdNames.end())
721 result = it->second(data,
end);
734 const size_t size = data.size();
738 while (i<size && data[i]!=c &&
739 data[i]!=
'\\' && data[i]!=
'@' &&
740 !(data[i]==
'/' && data[i-1]==
'<') &&
760 while (i+len<size && data[i+len]==c)
767 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
773 return static_cast<int>(i);
781 while (i < size && data[i] ==
'`')
789 while (i<size && enb<snb)
791 if (data[i]==
'`') enb++;
792 if (snb==1 && data[i]==
'\'')
break;
796 else if (data[i]==
'@' || data[i]==
'\\')
802 size_t l = endBlockName.
length();
805 if ((data[i]==
'\\' || data[i]==
'@') &&
806 data[i-1]!=
'\\' && data[i-1]!=
'@')
816 else if (i+1<size &&
isIdChar(data[i+1]))
825 else if (data[i-1]==
'<' && data[i]==
'/')
829 else if (data[i]==
'\n')
832 while (i<size && data[i]==
' ') i++;
833 if (i>=size || data[i]==
'\n')
851 const size_t size = data.size();
854 if (size>1 && data[0]==c && data[1]==c) { i=1; }
859 if (len==0) {
return 0; }
861 if (i>=size) {
return 0; }
863 if (i+1<size && data[i+1]==c)
868 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
874 return static_cast<int>(i+1);
885 const size_t size = data.size();
895 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
897 if (c ==
'~')
out+=
"<strike>";
898 else out+=
"<strong>";
900 if (c ==
'~')
out+=
"</strike>";
901 else out+=
"</strong>";
903 return static_cast<int>(i+2);
917 const size_t size = data.size();
929 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
934 if (i+2<size && data[i+1]==c && data[i+2]==c)
938 out+=
"</strong></em>";
940 return static_cast<int>(i+3);
942 else if (i+1<size && data[i+1]==c)
953 return static_cast<int>(len - 2);
967 return static_cast<int>(len - 1);
978 const size_t size = data.size();
982 if (i<size && data[i]==
'-')
987 if (i<size && data[i]==
'-')
992 if (i<size && data[i]==
'-')
996 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
998 if (count==2 && size > 2 && data[2]==
'>')
1000 if (count==3 && size > 3 && data[3]==
'>')
1002 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1022 const size_t size = data.size();
1025 while (i<size && data[i]!=
'"' && nl<2)
1027 if (data[i]==
'\n') nl++;
1030 if (i<size && data[i]==
'"' && nl<2)
1032 out+=data.substr(0,i+1);
1034 return static_cast<int>(i+1);
1046 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1048 const size_t size = data.size();
1054 while (i < size &&
isIdChar(data[i]))
1059 QCString tagName(data.substr(1,i-1));
1060 if (tagName.
lower()==
"pre")
1062 bool insideStr=
FALSE;
1066 if (!insideStr && c==
'<')
1068 if (data[i+1]==
'/' &&
1069 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1070 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1072 if (doWrite)
out+=data.substr(0,i+6);
1075 return static_cast<int>(i+6);
1078 else if (insideStr && c==
'"')
1080 if (data[i-1]!=
'\\') insideStr=
FALSE;
1093 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1096 if (doWrite)
out+=data.substr(0,i+2);
1098 return static_cast<int>(i+2);
1100 else if (data[i]==
'>')
1103 if (doWrite)
out+=data.substr(0,i+1);
1105 return static_cast<int>(i+1);
1107 else if (data[i]==
' ')
1110 bool insideAttr=
FALSE;
1113 if (!insideAttr && data[i]==
'"')
1117 else if (data[i]==
'"' && data[i-1]!=
'\\')
1121 else if (!insideAttr && data[i]==
'>')
1124 if (doWrite)
out+=data.substr(0,i+1);
1126 return static_cast<int>(i+1);
1146 const size_t size = data.size();
1158 if (size>2 && c!=
'~' && data[1]!=c)
1161 if (data[1]==
' ' || data[1]==
'\n' ||
1169 if (size>3 && data[1]==c && data[2]!=c)
1171 if (data[2]==
' ' || data[2]==
'\n' ||
1179 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1181 if (data[3]==
' ' || data[3]==
'\n' ||
1193 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1198 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1209 out+=link.
mid(fd ? 0 : 5);
1210 if (!explicitTitle && !content.
isEmpty())
1216 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1238 const size_t size = data.size();
1242 bool isImageLink =
FALSE;
1243 bool isImageInline =
FALSE;
1249 if (size<2 || data[1]!=
'[')
1258 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1260 if (data.data()[pos]==
'\n') numNLsNeeded--;
1261 else if (data.data()[pos]!=
' ')
1271 size_t contentStart=i;
1278 if (data[i-1]==
'\\')
1281 else if (data[i]==
'[')
1285 else if (data[i]==
']')
1288 if (level<=0)
break;
1290 else if (data[i]==
'\n')
1293 if (nl>1) {
return 0; }
1299 if (i>=size)
return 0;
1300 size_t contentEnd=i;
1301 content = data.substr(contentStart,contentEnd-contentStart);
1303 if (!isImageLink && content.
isEmpty()) {
return 0; }
1306 bool whiteSpace =
false;
1308 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1309 if (i<size && data[i]==
'\n')
1314 while (i<size && data[i]==
' ') i++;
1316 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1318 bool explicitTitle=
FALSE;
1319 if (i<size && data[i]==
'(')
1322 while (i<size && data[i]==
' ') i++;
1323 bool uriFormat=
false;
1324 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1328 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1334 if (nlConsec>1) {
return 0; }
1336 else if (data[i]==
'(')
1341 else if (data[i]==
')')
1346 else if (data[i]!=
' ')
1357 if (i>=size || data[i]==
'\n') {
return 0; }
1358 link = data.substr(linkStart,i-linkStart);
1361 if (link.
isEmpty()) {
return 0; }
1365 if (data[i]==
'\'' || data[i]==
'"')
1369 size_t titleStart=i;
1375 if (nl>1) {
return 0; }
1378 else if (data[i]==
'\\')
1382 else if (data[i]==c)
1393 size_t titleEnd = i-1;
1395 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1396 if (data[titleEnd]==c)
1398 title = data.substr(titleStart,titleEnd-titleStart);
1402 if (data[i]==
' ')i++;
1403 else if (data[i] ==
')')
break;
1417 else if (i<size && data[i]==
'[')
1423 while (i<size && data[i]!=
']')
1428 if (nl>1) {
return 0; }
1432 if (i>=size) {
return 0; }
1434 link = data.substr(linkStart,i-linkStart);
1446 link = lr_it->second.link;
1447 title = lr_it->second.title;
1457 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1464 link = lr_it->second.link;
1465 title = lr_it->second.title;
1469 else if (content==
"TOC")
1492 while (j<size && data[j]==
' ') { j++; }
1493 if (j<size && data[j]==
'{')
1498 size_t attributesStart=i;
1503 if (data[i-1]==
'\\')
1506 else if (data[i]==
'{')
1510 else if (data[i]==
'}')
1513 if (level<=0)
break;
1515 else if (data[i]==
'\n')
1518 if (nl>1) {
return 0; }
1523 if (i>=size)
return 0;
1524 size_t attributesEnd=i;
1525 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1534 while (pos<size && numNLsNeeded>0)
1536 if (data[pos]==
'\n') numNLsNeeded--;
1537 else if (data[pos]!=
' ')
1552 out+=
"@tableofcontents{html:";
1557 else if (isImageLink)
1561 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1566 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1567 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1568 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1569 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1570 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1592 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1624 if (explicitTitle && !title.
isEmpty())
1634 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1636 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1649 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1671 return static_cast<int>(i);
1678 const size_t size = data.size();
1682 while (nb<size && data[nb]==
'`')
1699 if (
end+1<size && data[
end+1]==
'`')
1714 if (
end+1<size && data[
end+1]==
'`')
1727 else if (data[
end]==
'\n')
1738 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1741 out+=data.substr(nb,
end-nb);
1744 return static_cast<int>(
end+1);
1746 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1749 out+=data.substr(nb,
end-nb);
1752 return static_cast<int>(
end+2);
1756 if (data[
end]!=
' ') pc = data[
end];
1760 if (i < nb && end >= size)
1765 out+=data.substr(0,nb);
1766 return static_cast<int>(nb);
1770 while (
end<size && data[
end]==
'`')
1780 QCString codeFragment = data.substr(nb,
end-nb-nb);
1786 return static_cast<int>(
end);
1805 const size_t size = data.size();
1811 size_t l = endBlockName.
length();
1814 if ((data[i]==
'\\' || data[i]==
'@') &&
1815 data[i-1]!=
'\\' && data[i-1]!=
'@')
1822 return static_cast<int>(i+1+l);
1831 out+=data.substr(0,endPos);
1832 return static_cast<int>(endPos);
1834 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1837 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1843 else if (c==
'\\' || c==
'@')
1845 out+=data.substr(0,2);
1849 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1851 out+=data.substr(1,3);
1855 else if (c==
'-' && size>2 && data[2]==
'-')
1857 out+=data.substr(1,2);
1871 const size_t size = data.size();
1877 out+=data.substr(i,
end-i);
1878 if (
end>=size)
break;
1881 int iend = action(*
this,data.substr(i),i);
1899 const size_t size = data.size();
1900 while (i<size && data[i]==
' ') i++;
1901 if (i==size)
return 0;
1906 while (i < size && data[i] ==
'=')
1911 while (i<size && data[i]==
' ') i++;
1912 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1913 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1928 while (i < size && data[i] ==
'-')
1933 while (i<size && data[i]==
' ') i++;
1934 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1944 const size_t size = data.size();
1945 while (i<size && data[i]==
' ') i++;
1950 while (i<size && (data[i]==
'>' || data[i]==
' '))
1952 if (data[i]==
'>') level++;
1957 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1972 const size_t size = data.size();
1975 while (i<size && data[i]==
' ') i++;
1976 if (i>=size || data[i]!=
'[') {
return 0; }
1978 size_t refIdStart=i;
1979 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1980 if (i>=size || data[i]!=
']') {
return 0; }
1981 refid = data.substr(refIdStart,i-refIdStart);
1982 if (refid.
isEmpty()) {
return 0; }
1986 if (i>=size || data[i]!=
':') {
return 0; }
1990 while (i<size && data[i]==
' ') i++;
1991 if (i<size && data[i]==
'\n')
1994 while (i<size && data[i]==
' ') i++;
1996 if (i>=size) {
return 0; }
1998 if (i<size && data[i]==
'<') i++;
2000 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2002 if (i<size && data[i]==
'>') i++;
2003 if (linkStart==linkEnd) {
return 0; }
2004 link = data.substr(linkStart,linkEnd-linkStart);
2006 if (link==
"@ref" || link==
"\\ref")
2009 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2010 link+=data.substr(argStart,i-argStart);
2017 while (i<size && data[i]==
' ') i++;
2018 if (i<size && data[i]==
'\n')
2022 while (i<size && data[i]==
' ') i++;
2026 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2031 if (c==
'\'' || c==
'"' || c==
'(')
2036 size_t titleStart=i;
2038 while (i<size && data[i]!=
'\n') i++;
2043 while (
end>titleStart && data[
end]!=c)
end--;
2046 title = data.substr(titleStart,
end-titleStart);
2050 while (i<size && data[i]==
' ') i++;
2062 size_t size = data.size();
2063 if (size>0 && data[size-1]==
'\n') size--;
2064 while (i<size && data[i]==
' ') i++;
2065 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2067 if (c!=
'*' && c!=
'-' && c!=
'_')
2079 else if (data[i]!=
' ')
2094 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2096 std::string ti = title.str();
2099 std::string
id = match[1].str();
2100 title = title.
left(match.position());
2103 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2109 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2112 if (pIsIdGenerated) *pIsIdGenerated=
true;
2127 int level = 0, blanks=0;
2128 const size_t size = data.size();
2131 while (i<size && data[i]==
' ') i++;
2132 if (i>=size || data[i]!=
'#')
2136 while (i < size && data[i] ==
'#')
2145 while (i < size && data[i] ==
' ')
2150 if (level==1 && blanks==0)
2157 while (
end<size && data[
end]!=
'\n')
end++;
2158 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2161 header = data.substr(i,
end-i);
2165 int idx=
static_cast<int>(header.
length())-1;
2166 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2167 header=header.
left(idx+1);
2170 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2201 while (i<data.size())
2212 (data[(i)]=='<' && \
2213 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2214 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2223 const size_t size=data.size();
2227 bool listMarkerSkipped=
FALSE;
2230 (!listMarkerSkipped &&
2231 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2232 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2233 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2234 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2243 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2247 if (j+1<size && data[j+1]==
' ')
2249 listMarkerSkipped=
TRUE;
2266 listMarkerSkipped=
TRUE;
2268 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2270 listMarkerSkipped=
TRUE;
2274 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2276 listMarkerSkipped=
TRUE;
2278 if (data[i]!=
' ' && !listMarkerSkipped)
2292 size_t normalIndent = 0;
2293 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2295 size_t result = listIndent>normalIndent ? listIndent : 0;
2306 while (i<data.size())
2312 else if (data[i]==
'\n')
2316 else if (data[i]!=
' ' && data[i]!=
'\t')
2328 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2332 const char dot =
'.';
2333 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2334 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2335 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2341 const size_t size = data.size();
2342 while (i < size && data[i] ==
' ')
2347 if (indent>=refIndent+4)
2349 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2353 if (i<size && data[i]==
'`') tildaChar=
'`';
2354 while (i < size && data[i] == tildaChar)
2361 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2364 if (i<size && data[i]==
'{')
2367 if (data[i] == dot) i++;
2369 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2370 if (i<size && data[i]==
'}')
2372 lang = data.substr(startLang,i-startLang);
2381 else if (i<size && isLangChar(data[i]))
2383 if (data[i] == dot) i++;
2385 if (i<size && isAlphaChar(data[i]))
2388 while (i<size && isAlphaNChar(data[i])) i++;
2390 lang = data.substr(startLang,i-startLang);
2400 if (data[i]==tildaChar)
2404 while (i < size && data[i] == tildaChar)
2409 while (i<size && data[i]==
' ') i++;
2411 if (endTildes==startTildes)
2414 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2421 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2426static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2433 const size_t size = data.size();
2434 while (i < size && data[i] ==
' ')
2442 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2445 if (indent0>=size || data[indent0]==
'\n')
2447 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2454 int offset_i =
static_cast<int>(offset);
2458 int j =
static_cast<int>(i)-offset_i-1;
2460 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2463 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2469 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2480 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2489 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2495 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2502 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2510 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2526 const size_t size = data.size();
2529 while (i<size && data[i]==
' ') i++;
2530 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2539 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2542 if (j>0 && i>0) i--;
2543 while (i>0 && data[i]==
' ') i--;
2544 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2558 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2559 if (columns==1) columns++;
2563 if (n==2 && columns==0)
2575 size_t cc0=0, start=0,
end=0;
2579 if (i>=data.size() || cc0<1)
2591 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2600 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2615 const size_t size = data.size();
2617 size_t columns=0, start=0,
end=0;
2619 size_t headerStart = start;
2620 size_t headerEnd =
end;
2626 std::vector<Alignment> columnAlignment(columns);
2628 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2634 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2635 if (data[j]==
'-') startFound=
TRUE;
2638 if (data[j]==
'-') rightMarker=
FALSE;
2639 else if (data[j]==
':') rightMarker=
TRUE;
2640 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2664 std::vector<std::vector<TableCell> > tableContents;
2666 size_t m = headerStart;
2667 std::vector<TableCell> headerContents(columns);
2668 for (k=0;k<columns;k++)
2670 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2672 headerContents[k].cellText += data[m++];
2677 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2678 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2680 tableContents.push_back(headerContents);
2686 if (cc!=columns)
break;
2690 std::vector<TableCell> rowContents(columns);
2693 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2697 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2698 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2703 rowContents[k].cellText += data[j];
2709 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2710 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2711 tableContents.push_back(rowContents);
2717 out+=
"<table class=\"markdownTable\">";
2718 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2719 for (
size_t row = 0; row < tableContents.size(); row++)
2725 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2729 out+=
"\n<tr class=\"markdownTableRowEven\">";
2734 out+=
"\n <tr class=\"markdownTableHead\">";
2736 for (
size_t c = 0; c < columns; c++)
2739 QCString cellText(tableContents[row][c].cellText);
2745 if (tableContents[row][c].cellText ==
"^")
2749 if (tableContents[row][c].colSpan)
2751 int cr =
static_cast<int>(c);
2752 while ( cr >= 0 && tableContents[row][cr].colSpan)
2756 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2758 size_t rowSpan = 1, spanRow = row+1;
2759 while ((spanRow < tableContents.size()) &&
2760 (tableContents[spanRow][c].cellText ==
"^"))
2766 out+=
" <" + cellTag +
" " + cellClass;
2768 switch (columnAlignment[c])
2780 out+=
" rowspan=\"" + spanStr +
"\"";
2786 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2795 out+=
" colspan=\"" + spanStr +
"\"";
2799 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2802 cellClass =
"class=\"markdownTableBody";
2818 while (i<data.size() && data[i]!=
'\n')
2820 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2823 if (i>=data.size()) {
return 0; }
2824 if (i<2) {
return 0; }
2825 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2865 out+=
"</"+hTag+
">\n";
2868 else if (data.size()>0)
2870 size_t tmpSize = data.size();
2871 if (data[data.size()-1] ==
'\n') tmpSize--;
2872 out+=data.substr(0,tmpSize);
2876 out+=
"\\ilinebr<br>";
2878 if (tmpSize != data.size())
out+=
'\n';
2884 {
"[!note]",
"\\note" },
2885 {
"[!warning]",
"\\warning" },
2886 {
"[!tip]",
"\\remark" },
2887 {
"[!caution]",
"\\attention" },
2888 {
"[!important]",
"\\important" }
2897 const size_t size = data.size();
2898 std::string startCmd;
2899 int isGitHubAlert =
false;
2900 int isGitHubFirst =
false;
2905 while (
end<=size && data[
end-1]!=
'\n')
end++;
2910 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2912 if (data[j]==
'>') { level++; indent=j+1; }
2913 else if (j>0 && data[j-1]==
'>') indent=j+1;
2916 if (indent>0 && j>0 && data[j-1]==
'>' &&
2917 !(j==size || data[j]==
'\n'))
2934 isGitHubAlert =
true;
2935 isGitHubFirst =
true;
2936 startCmd = it->second;
2941 if (level!=1 || !isGitHubAlert)
2943 for (
int l=curLevel;l<level-1;l++)
2945 out+=
"<blockquote>";
2947 out +=
"<blockquote>‍";
2949 else if (!startCmd.empty())
2951 out += startCmd +
" ";
2954 else if (level<curLevel)
2957 if (level==0 && isGitHubAlert)
2963 out +=
"</blockquote>\\ilinebr ";
2972 if (curLevel!=0 || !isGitHubAlert)
2974 std::string_view txt = data.substr(indent,
end-indent);
2977 if (!isGitHubFirst)
out +=
"<br>";
2984 isGitHubFirst =
false;
2999 for (
int l=0;l<curLevel;l++)
3001 out+=
"</blockquote>";
3012 size_t size = data.size();
3013 while (i<data.size() && data[i]==
' ') i++;
3016 size_t locStart = i;
3017 if (i>offset) locStart--;
3020 while (i+9<size && data[i]!=
'\n')
3032 location=data.substr(locStart,i-locStart);
3034 while (indent > 0 && i < size && data[i] ==
' ')
3039 if (i<size && data[i]==
'\n') i++;
3050 const size_t size = data.size();
3053 out+=
"@iverbatim\n";
3055 std::string location;
3060 while (
end<=size && data[
end-1]!=
'\n')
end++;
3063 while (j <
end && data[j] ==
' ')
3077 while (emptyLines>0)
3085 std::string lineLoc;
3090 out+=data.substr(offset,
end-offset);
3098 out+=
"@endiverbatim";
3099 if (!location.empty())
3107 while (emptyLines>0)
3123 const size_t size = data.size();
3124 size_t nb=0,
end=offset+1, j=0;
3129 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3130 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3137 size_t l = endBlockName.
length();
3138 for (;
end+l+1<size;
end++)
3140 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3141 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3155 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3156 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3159 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3160 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3171 else if (nb==0 && data[
end-1]==
'`')
3173 while (
end <= size && data[
end - 1] ==
'`')
3179 else if (nb>0 && data[
end-1]==
'`')
3182 while (
end <= size && data[
end - 1] ==
'`')
3200 size_t blockStart,
size_t blockEnd)
3203 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3204 const size_t size=data.size();
3206 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3227 size_t pi=std::string::npos;
3228 bool newBlock =
false;
3229 bool insideList =
false;
3230 size_t currentIndent = refIndent;
3231 size_t listIndent = refIndent;
3232 const size_t size = data.size();
3239 size_t lineIndent=0;
3240 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3246 if (insideList && lineIndent<currentIndent)
3249 currentIndent = refIndent;
3257 if (listIndent<currentIndent+4)
3261 currentIndent = listIndent;
3268 currentIndent = listIndent;
3277 if (pi!=std::string::npos)
3279 size_t blockStart=0, blockEnd=0, blockOffset=0;
3282 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3284 size_t cmdPos = pi+blockStart+1;
3285 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3291 if (pl[ii]==
'\n') nl++;
3294 bool addNewLines =
false;
3296 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3305 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3306 addNewLines =
false;
3321 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3323 if (addNewLines)
out+=
'\n';
3328 addSpecialCommand(
"startuml",
"enduml");
3332 addSpecialCommand(
"dot",
"enddot");
3334 else if (lang==
"msc")
3336 addSpecialCommand(
"msc",
"endmsc");
3343 pi=std::string::npos;
3347 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3350 pi=std::string::npos;
3357 out+=data.substr(pi,i-pi);
3363 if (pi!=std::string::npos && pi<size)
3373 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3375 out+=data.substr(pi);
3389 size_t pi = std::string::npos;
3405 size_t currentIndent = indent;
3406 size_t listIndent = indent;
3407 bool insideList =
false;
3408 bool newBlock =
false;
3411 while (i<data.size())
3416 size_t lineIndent=0;
3418 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3424 if (insideList && lineIndent<currentIndent)
3427 currentIndent = indent;
3435 if (listIndent<currentIndent+4)
3439 currentIndent = listIndent;
3446 currentIndent = listIndent;
3458 if (pi!=std::string::npos)
3460 size_t blockStart=0, blockEnd=0, blockOffset=0;
3462 size_t blockIndent = currentIndent;
3466 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3470 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3480 size_t l = endBlockName.
length();
3481 while (i+l<data.size())
3483 if ((data[i]==
'\\' || data[i]==
'@') &&
3484 data[i-1]!=
'\\' && data[i-1]!=
'@')
3501 while (pi<data.size() && data[pi]==
' ') pi++;
3502 QCString header = data.substr(pi,i-pi-1);
3509 out+=level==1?
"@section ":
"@subsection ";
3517 out+=level==1?
"<h1>":
"<h2>";
3519 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3526 pi=std::string::npos;
3531 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3545 pi=std::string::npos;
3553 pi=std::string::npos;
3560 pi=std::string::npos;
3573 if (pi!=std::string::npos && pi<data.size())
3575 if (
isLinkRef(data.substr(pi),
id,link,title))
3592#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3593 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3596 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3597 OPC(idlexcept);
OPC(file);
3607 std::string_view data(docs.
str());
3608 const size_t size = data.size();
3611 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3618 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3624 (data[i]==
'\\' || data[i]==
'@') &&
3639 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3657 std::string_view data(docs_org.
str());
3658 const size_t size = data.size();
3660 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3662 if (data[i]==
'\n') prepend++;
3665 if (i>=size) {
return QCString(); }
3667 while (end1<size && data[end1-1]!=
'\n') end1++;
3674 while (end2<size && data[end2-1]!=
'\n') end2++;
3675 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3677 title = data.substr(i,end1-i-1);
3678 docs+=
"\n\n"+docs_org.
mid(end2);
3679 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3685 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3688 docs+=docs_org.
mid(end1);
3693 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3704 if (input.
isEmpty())
return input;
3709 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3710 s =
detab(s,refIndent);
3714 s =
prv->processQuotations(s.
view(),refIndent);
3718 s =
prv->processBlocks(s.
view(),refIndent);
3736 const char *p = result.
data();
3739 while (*p==
' ') p++;
3740 while (*p==
'\n') {startNewlines++;p++;};
3743 if (p>result.
data())
3746 result = result.
mid(
static_cast<int>(p-result.
data()));
3759 if (i!=-1) baseFn = baseFn.
left(i);
3783 const char *fileBuf,
3784 const std::shared_ptr<Entry> &root,
3787 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3789 current->lang = SrcLangExt::Markdown;
3790 current->fileName = fileName;
3791 current->docFile = fileName;
3792 current->docLine = 1;
3798 bool isIdGenerated =
false;
3806 int indentLevel=title.
isEmpty() ? 0 : -1;
3813 bool wasEmpty =
id.isEmpty();
3814 if (wasEmpty)
id = mdFileNameId;
3820 if (!mdfileAsMainPage.
isEmpty() &&
3824 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3825 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3827 else if (
id==
"mainpage" ||
id==
"index")
3829 if (title.
isEmpty()) title = titleFn;
3830 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3831 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3833 else if (isSubdirDocs)
3837 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3839 docs.
prepend(
"@dir\\ilinebr ");
3850 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3852 else if (!generatedId.
isEmpty())
3854 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3856 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3859 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3861 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3863 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3868 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3870 std::string s = docs.str();
3873 QCString orgLabel = match[1].str();
3874 QCString orgTitle = match[2].str();
3877 docs = docs.
left(match[1].position())+
3880 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3892 p->commentScanner.enterFile(fileName,lineNr);
3894 bool needsEntry =
false;
3898 while (
p->commentScanner.parseCommentBlock(
3916 QCString docFile = current->docFile;
3917 root->moveToSubEntryAndRefresh(current);
3918 current->lang = SrcLangExt::Markdown;
3919 current->docFile = docFile;
3920 current->docLine = lineNr;
3925 root->moveToSubEntryAndKeep(current);
3927 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.