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==
'\'' ||
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; }
1327 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1332 if (nl>1) {
return 0; }
1334 else if (data[i]==
'(')
1338 else if (data[i]==
')')
1349 if (i>=size || data[i]==
'\n') {
return 0; }
1350 link = data.substr(linkStart,i-linkStart);
1353 if (link.
isEmpty()) {
return 0; }
1357 if (data[i]==
'\'' || data[i]==
'"')
1361 size_t titleStart=i;
1367 if (nl>1) {
return 0; }
1370 else if (data[i]==
'\\')
1374 else if (data[i]==c)
1385 size_t titleEnd = i-1;
1387 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1388 if (data[titleEnd]==c)
1390 title = data.substr(titleStart,titleEnd-titleStart);
1394 if (data[i]==
' ')i++;
1395 else if (data[i] ==
')')
break;
1409 else if (i<size && data[i]==
'[')
1415 while (i<size && data[i]!=
']')
1420 if (nl>1) {
return 0; }
1424 if (i>=size) {
return 0; }
1426 link = data.substr(linkStart,i-linkStart);
1438 link = lr_it->second.link;
1439 title = lr_it->second.title;
1449 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1456 link = lr_it->second.link;
1457 title = lr_it->second.title;
1461 else if (content==
"TOC")
1484 while (j<size && data[j]==
' ') { j++; }
1485 if (j<size && data[j]==
'{')
1490 size_t attributesStart=i;
1495 if (data[i-1]==
'\\')
1498 else if (data[i]==
'{')
1502 else if (data[i]==
'}')
1505 if (level<=0)
break;
1507 else if (data[i]==
'\n')
1510 if (nl>1) {
return 0; }
1515 if (i>=size)
return 0;
1516 size_t attributesEnd=i;
1517 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1526 while (pos<size && numNLsNeeded>0)
1528 if (data[pos]==
'\n') numNLsNeeded--;
1529 else if (data[pos]!=
' ')
1544 out+=
"@tableofcontents{html:";
1549 else if (isImageLink)
1553 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1558 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1559 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1560 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1561 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1562 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1584 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1616 if (explicitTitle && !title.
isEmpty())
1626 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1628 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1641 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1663 return static_cast<int>(i);
1670 const size_t size = data.size();
1674 while (nb<size && data[nb]==
'`')
1691 if (
end+1<size && data[
end+1]==
'`')
1706 if (
end+1<size && data[
end+1]==
'`')
1719 else if (data[
end]==
'\n')
1730 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1733 out+=data.substr(nb,
end-nb);
1736 return static_cast<int>(
end+1);
1738 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1741 out+=data.substr(nb,
end-nb);
1744 return static_cast<int>(
end+2);
1748 if (data[
end]!=
' ') pc = data[
end];
1752 if (i < nb && end >= size)
1757 out+=data.substr(0,nb);
1758 return static_cast<int>(nb);
1762 while (
end<size && data[
end]==
'`')
1772 QCString codeFragment = data.substr(nb,
end-nb-nb);
1778 return static_cast<int>(
end);
1797 const size_t size = data.size();
1803 size_t l = endBlockName.
length();
1806 if ((data[i]==
'\\' || data[i]==
'@') &&
1807 data[i-1]!=
'\\' && data[i-1]!=
'@')
1814 return static_cast<int>(i+1+l);
1823 out+=data.substr(0,endPos);
1824 return static_cast<int>(endPos);
1826 if (size>1 && data[0]==
'\\')
1829 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1835 else if (c==
'\\' || c==
'@')
1837 out+=data.substr(0,2);
1841 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1843 out+=data.substr(1,3);
1847 else if (c==
'-' && size>2 && data[2]==
'-')
1849 out+=data.substr(1,2);
1854 else if (size>1 && data[0]==
'@')
1857 if (c==
'\\' || c==
'@')
1859 out+=data.substr(0,2);
1873 const size_t size = data.size();
1879 out+=data.substr(i,
end-i);
1880 if (
end>=size)
break;
1883 int iend = action(*
this,data.substr(i),i);
1901 const size_t size = data.size();
1902 while (i<size && data[i]==
' ') i++;
1903 if (i==size)
return 0;
1908 while (i < size && data[i] ==
'=')
1913 while (i<size && data[i]==
' ') i++;
1914 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1915 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1930 while (i < size && data[i] ==
'-')
1935 while (i<size && data[i]==
' ') i++;
1936 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1946 const size_t size = data.size();
1947 while (i<size && data[i]==
' ') i++;
1952 while (i<size && (data[i]==
'>' || data[i]==
' '))
1954 if (data[i]==
'>') level++;
1959 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1974 const size_t size = data.size();
1977 while (i<size && data[i]==
' ') i++;
1978 if (i>=size || data[i]!=
'[') {
return 0; }
1980 size_t refIdStart=i;
1981 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1982 if (i>=size || data[i]!=
']') {
return 0; }
1983 refid = data.substr(refIdStart,i-refIdStart);
1984 if (refid.
isEmpty()) {
return 0; }
1988 if (i>=size || data[i]!=
':') {
return 0; }
1992 while (i<size && data[i]==
' ') i++;
1993 if (i<size && data[i]==
'\n')
1996 while (i<size && data[i]==
' ') i++;
1998 if (i>=size) {
return 0; }
2000 if (i<size && data[i]==
'<') i++;
2002 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2004 if (i<size && data[i]==
'>') i++;
2005 if (linkStart==linkEnd) {
return 0; }
2006 link = data.substr(linkStart,linkEnd-linkStart);
2008 if (link==
"@ref" || link==
"\\ref")
2011 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2012 link+=data.substr(argStart,i-argStart);
2019 while (i<size && data[i]==
' ') i++;
2020 if (i<size && data[i]==
'\n')
2024 while (i<size && data[i]==
' ') i++;
2028 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2033 if (c==
'\'' || c==
'"' || c==
'(')
2038 size_t titleStart=i;
2040 while (i<size && data[i]!=
'\n') i++;
2045 while (
end>titleStart && data[
end]!=c)
end--;
2048 title = data.substr(titleStart,
end-titleStart);
2052 while (i<size && data[i]==
' ') i++;
2064 size_t size = data.size();
2065 if (size>0 && data[size-1]==
'\n') size--;
2066 while (i<size && data[i]==
' ') i++;
2067 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2069 if (c!=
'*' && c!=
'-' && c!=
'_')
2081 else if (data[i]!=
' ')
2096 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2098 std::string ti = title.str();
2101 std::string
id = match[1].str();
2102 title = title.
left(match.position());
2105 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2111 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2114 if (pIsIdGenerated) *pIsIdGenerated=
true;
2129 int level = 0, blanks=0;
2130 const size_t size = data.size();
2133 while (i<size && data[i]==
' ') i++;
2134 if (i>=size || data[i]!=
'#')
2138 while (i < size && data[i] ==
'#')
2147 while (i < size && data[i] ==
' ')
2152 if (level==1 && blanks==0)
2159 while (
end<size && data[
end]!=
'\n')
end++;
2160 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2163 header = data.substr(i,
end-i);
2167 int idx=
static_cast<int>(header.
length())-1;
2168 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2169 header=header.
left(idx+1);
2172 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2203 while (i<data.size())
2214 (data[(i)]=='<' && \
2215 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2216 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2225 const size_t size=data.size();
2229 bool listMarkerSkipped=
FALSE;
2232 (!listMarkerSkipped &&
2233 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2234 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2235 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2236 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2245 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2249 if (j+1<size && data[j+1]==
' ')
2251 listMarkerSkipped=
TRUE;
2268 listMarkerSkipped=
TRUE;
2270 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2272 listMarkerSkipped=
TRUE;
2276 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2278 listMarkerSkipped=
TRUE;
2280 if (data[i]!=
' ' && !listMarkerSkipped)
2294 size_t normalIndent = 0;
2295 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2297 size_t result = listIndent>normalIndent ? listIndent : 0;
2308 while (i<data.size())
2314 else if (data[i]==
'\n')
2318 else if (data[i]!=
' ' && data[i]!=
'\t')
2330 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2334 const char dot =
'.';
2335 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2336 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2337 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2343 const size_t size = data.size();
2344 while (i < size && data[i] ==
' ')
2349 if (indent>=refIndent+4)
2351 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2355 if (i<size && data[i]==
'`') tildaChar=
'`';
2356 while (i < size && data[i] == tildaChar)
2363 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2366 if (i<size && data[i]==
'{')
2369 if (data[i] == dot) i++;
2371 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2372 if (i<size && data[i]==
'}')
2374 lang = data.substr(startLang,i-startLang);
2383 else if (i<size && isLangChar(data[i]))
2385 if (data[i] == dot) i++;
2387 if (i<size && isAlphaChar(data[i]))
2390 while (i<size && isAlphaNChar(data[i])) i++;
2392 lang = data.substr(startLang,i-startLang);
2402 if (data[i]==tildaChar)
2406 while (i < size && data[i] == tildaChar)
2411 while (i<size && data[i]==
' ') i++;
2413 if (endTildes==startTildes)
2416 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2423 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2428static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2435 const size_t size = data.size();
2436 while (i < size && data[i] ==
' ')
2444 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2447 if (indent0>=size || data[indent0]==
'\n')
2449 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2456 int offset_i =
static_cast<int>(offset);
2460 int j =
static_cast<int>(i)-offset_i-1;
2462 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2465 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2471 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2482 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2491 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2497 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2504 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2512 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2528 const size_t size = data.size();
2531 while (i<size && data[i]==
' ') i++;
2532 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2541 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2544 if (j>0 && i>0) i--;
2545 while (i>0 && data[i]==
' ') i--;
2546 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2560 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2561 if (columns==1) columns++;
2565 if (n==2 && columns==0)
2577 size_t cc0=0, start=0,
end=0;
2581 if (i>=data.size() || cc0<1)
2593 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2602 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2617 const size_t size = data.size();
2619 size_t columns=0, start=0,
end=0;
2621 size_t headerStart = start;
2622 size_t headerEnd =
end;
2628 std::vector<Alignment> columnAlignment(columns);
2630 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2636 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2637 if (data[j]==
'-') startFound=
TRUE;
2640 if (data[j]==
'-') rightMarker=
FALSE;
2641 else if (data[j]==
':') rightMarker=
TRUE;
2642 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2666 std::vector<std::vector<TableCell> > tableContents;
2668 size_t m = headerStart;
2669 std::vector<TableCell> headerContents(columns);
2670 for (k=0;k<columns;k++)
2672 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2674 headerContents[k].cellText += data[m++];
2679 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2680 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2682 tableContents.push_back(headerContents);
2688 if (cc!=columns)
break;
2692 std::vector<TableCell> rowContents(columns);
2695 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2699 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2700 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2705 rowContents[k].cellText += data[j];
2711 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2712 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2713 tableContents.push_back(rowContents);
2719 out+=
"<table class=\"markdownTable\">";
2720 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2721 for (
size_t row = 0; row < tableContents.size(); row++)
2727 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2731 out+=
"\n<tr class=\"markdownTableRowEven\">";
2736 out+=
"\n <tr class=\"markdownTableHead\">";
2738 for (
size_t c = 0; c < columns; c++)
2741 QCString cellText(tableContents[row][c].cellText);
2747 if (tableContents[row][c].cellText ==
"^")
2751 if (tableContents[row][c].colSpan)
2753 int cr =
static_cast<int>(c);
2754 while ( cr >= 0 && tableContents[row][cr].colSpan)
2758 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2760 size_t rowSpan = 1, spanRow = row+1;
2761 while ((spanRow < tableContents.size()) &&
2762 (tableContents[spanRow][c].cellText ==
"^"))
2768 out+=
" <" + cellTag +
" " + cellClass;
2770 switch (columnAlignment[c])
2782 out+=
" rowspan=\"" + spanStr +
"\"";
2788 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2797 out+=
" colspan=\"" + spanStr +
"\"";
2801 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2804 cellClass =
"class=\"markdownTableBody";
2820 while (i<data.size() && data[i]!=
'\n')
2822 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2825 if (i>=data.size()) {
return 0; }
2826 if (i<2) {
return 0; }
2827 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2867 out+=
"</"+hTag+
">\n";
2870 else if (data.size()>0)
2872 size_t tmpSize = data.size();
2873 if (data[data.size()-1] ==
'\n') tmpSize--;
2874 out+=data.substr(0,tmpSize);
2878 out+=
"\\ilinebr<br>";
2880 if (tmpSize != data.size())
out+=
'\n';
2886 {
"[!note]",
"\\note" },
2887 {
"[!warning]",
"\\warning" },
2888 {
"[!tip]",
"\\remark" },
2889 {
"[!caution]",
"\\attention" },
2890 {
"[!important]",
"\\important" }
2899 const size_t size = data.size();
2900 std::string startCmd;
2901 int isGitHubAlert =
false;
2902 int isGitHubFirst =
false;
2907 while (
end<=size && data[
end-1]!=
'\n')
end++;
2912 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2914 if (data[j]==
'>') { level++; indent=j+1; }
2915 else if (j>0 && data[j-1]==
'>') indent=j+1;
2918 if (indent>0 && j>0 && data[j-1]==
'>' &&
2919 !(j==size || data[j]==
'\n'))
2936 isGitHubAlert =
true;
2937 isGitHubFirst =
true;
2938 startCmd = it->second;
2943 if (level!=1 || !isGitHubAlert)
2945 for (
int l=curLevel;l<level-1;l++)
2947 out+=
"<blockquote>";
2949 out +=
"<blockquote>‍";
2951 else if (!startCmd.empty())
2953 out += startCmd +
" ";
2956 else if (level<curLevel)
2959 if (level==0 && isGitHubAlert)
2965 out +=
"</blockquote>\\ilinebr ";
2974 if (curLevel!=0 || !isGitHubAlert)
2976 std::string_view txt = data.substr(indent,
end-indent);
2979 if (!isGitHubFirst)
out +=
"<br>";
2986 isGitHubFirst =
false;
3001 for (
int l=0;l<curLevel;l++)
3003 out+=
"</blockquote>";
3014 size_t size = data.size();
3015 while (i<data.size() && data[i]==
' ') i++;
3018 size_t locStart = i;
3019 if (i>offset) locStart--;
3022 while (i+9<size && data[i]!=
'\n')
3034 location=data.substr(locStart,i-locStart);
3036 while (indent > 0 && i < size && data[i] ==
' ')
3041 if (i<size && data[i]==
'\n') i++;
3052 const size_t size = data.size();
3055 out+=
"@iverbatim\n";
3057 std::string location;
3062 while (
end<=size && data[
end-1]!=
'\n')
end++;
3065 while (j <
end && data[j] ==
' ')
3079 while (emptyLines>0)
3087 std::string lineLoc;
3092 out+=data.substr(offset,
end-offset);
3100 out+=
"@endiverbatim";
3101 if (!location.empty())
3109 while (emptyLines>0)
3125 const size_t size = data.size();
3126 size_t nb=0,
end=offset+1, j=0;
3131 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3132 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3139 size_t l = endBlockName.
length();
3140 for (;
end+l+1<size;
end++)
3142 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3143 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3157 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3158 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3161 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3162 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3173 else if (nb==0 && data[
end-1]==
'`')
3175 while (
end <= size && data[
end - 1] ==
'`')
3181 else if (nb>0 && data[
end-1]==
'`')
3184 while (
end <= size && data[
end - 1] ==
'`')
3202 size_t blockStart,
size_t blockEnd)
3205 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3206 const size_t size=data.size();
3208 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3229 size_t pi=std::string::npos;
3230 bool newBlock =
false;
3231 bool insideList =
false;
3232 size_t currentIndent = refIndent;
3233 size_t listIndent = refIndent;
3234 const size_t size = data.size();
3241 size_t lineIndent=0;
3242 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3248 if (insideList && lineIndent<currentIndent)
3251 currentIndent = refIndent;
3259 if (listIndent<currentIndent+4)
3263 currentIndent = listIndent;
3270 currentIndent = listIndent;
3279 if (pi!=std::string::npos)
3281 size_t blockStart=0, blockEnd=0, blockOffset=0;
3284 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3286 size_t cmdPos = pi+blockStart+1;
3287 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3293 if (pl[ii]==
'\n') nl++;
3296 bool addNewLines =
false;
3298 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3307 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3308 addNewLines =
false;
3323 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3325 if (addNewLines)
out+=
'\n';
3330 addSpecialCommand(
"startuml",
"enduml");
3334 addSpecialCommand(
"dot",
"enddot");
3336 else if (lang==
"msc")
3338 addSpecialCommand(
"msc",
"endmsc");
3345 pi=std::string::npos;
3349 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3352 pi=std::string::npos;
3359 out+=data.substr(pi,i-pi);
3365 if (pi!=std::string::npos && pi<size)
3375 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3377 out+=data.substr(pi);
3391 size_t pi = std::string::npos;
3407 size_t currentIndent = indent;
3408 size_t listIndent = indent;
3409 bool insideList =
false;
3410 bool newBlock =
false;
3413 while (i<data.size())
3418 size_t lineIndent=0;
3420 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3426 if (insideList && lineIndent<currentIndent)
3429 currentIndent = indent;
3437 if (listIndent<currentIndent+4)
3441 currentIndent = listIndent;
3448 currentIndent = listIndent;
3460 if (pi!=std::string::npos)
3462 size_t blockStart=0, blockEnd=0, blockOffset=0;
3464 size_t blockIndent = currentIndent;
3468 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3472 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3482 size_t l = endBlockName.
length();
3483 while (i+l<data.size())
3485 if ((data[i]==
'\\' || data[i]==
'@') &&
3486 data[i-1]!=
'\\' && data[i-1]!=
'@')
3503 while (pi<data.size() && data[pi]==
' ') pi++;
3504 QCString header = data.substr(pi,i-pi-1);
3511 out+=level==1?
"@section ":
"@subsection ";
3519 out+=level==1?
"<h1>":
"<h2>";
3521 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3528 pi=std::string::npos;
3533 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3547 pi=std::string::npos;
3555 pi=std::string::npos;
3562 pi=std::string::npos;
3575 if (pi!=std::string::npos && pi<data.size())
3577 if (
isLinkRef(data.substr(pi),
id,link,title))
3594#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3595 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3598 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3599 OPC(idlexcept);
OPC(file);
3609 std::string_view data(docs.
str());
3610 const size_t size = data.size();
3613 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3620 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3626 (data[i]==
'\\' || data[i]==
'@') &&
3641 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3659 std::string_view data(docs_org.
str());
3660 const size_t size = data.size();
3662 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3664 if (data[i]==
'\n') prepend++;
3667 if (i>=size) {
return QCString(); }
3669 while (end1<size && data[end1-1]!=
'\n') end1++;
3676 while (end2<size && data[end2-1]!=
'\n') end2++;
3677 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3679 title = data.substr(i,end1-i-1);
3680 docs+=
"\n\n"+docs_org.
mid(end2);
3681 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3687 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3690 docs+=docs_org.
mid(end1);
3695 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3706 if (input.
isEmpty())
return input;
3711 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3712 s =
detab(s,refIndent);
3716 s =
prv->processQuotations(s.
view(),refIndent);
3720 s =
prv->processBlocks(s.
view(),refIndent);
3738 const char *p = result.
data();
3741 while (*p==
' ') p++;
3742 while (*p==
'\n') {startNewlines++;p++;};
3745 if (p>result.
data())
3748 result = result.
mid(
static_cast<int>(p-result.
data()));
3761 if (i!=-1) baseFn = baseFn.
left(i);
3785 const char *fileBuf,
3786 const std::shared_ptr<Entry> &root,
3789 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3791 current->lang = SrcLangExt::Markdown;
3792 current->fileName = fileName;
3793 current->docFile = fileName;
3794 current->docLine = 1;
3800 bool isIdGenerated =
false;
3808 int indentLevel=title.
isEmpty() ? 0 : -1;
3815 bool wasEmpty =
id.isEmpty();
3816 if (wasEmpty)
id = mdFileNameId;
3822 if (!mdfileAsMainPage.
isEmpty() &&
3826 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3827 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3829 else if (
id==
"mainpage" ||
id==
"index")
3831 if (title.
isEmpty()) title = titleFn;
3832 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3833 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3835 else if (isSubdirDocs)
3839 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3841 docs.
prepend(
"@dir\\ilinebr ");
3852 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3854 else if (!generatedId.
isEmpty())
3856 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3858 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3861 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3863 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3865 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3870 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3872 std::string s = docs.str();
3875 QCString orgLabel = match[1].str();
3876 QCString orgTitle = match[2].str();
3879 docs = docs.
left(match[1].position())+
3882 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3894 p->commentScanner.enterFile(fileName,lineNr);
3896 bool needsEntry =
false;
3900 while (
p->commentScanner.parseCommentBlock(
3918 QCString docFile = current->docFile;
3919 root->moveToSubEntryAndRefresh(current);
3920 current->lang = SrcLangExt::Markdown;
3921 current->docFile = docFile;
3922 current->docLine = lineNr;
3927 root->moveToSubEntryAndKeep(current);
3929 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.