36#include <unordered_map>
59#if !ENABLE_MARKDOWN_TRACING
63#define AUTO_TRACE(...) (void)0
64#define AUTO_TRACE_ADD(...) (void)0
65#define AUTO_TRACE_EXIT(...) (void)0
81 return (c>=
'a' && c<=
'z') ||
84 (
static_cast<unsigned char>(c)>=0x80);
90 return c==
'-' || c==
'+' || c==
'!' || c==
'?' || c==
'$' || c==
'@' ||
91 c==
'&' || c==
'*' || c==
'_' || c==
'%' || c==
'[' || c==
'(' ||
92 c==
'.' || c==
'>' || c==
':' || c==
',' || c==
';' || c==
'\'' ||
93 c==
'"' || c==
'`' || c==
'\\';
99 return c==
'\n' || c==
' ' || c==
'\'' || c==
'<' ||
100 c==
'>' || c==
'{' || c==
'(' || c==
'[' ||
101 c==
',' || c==
':' || c==
';';
107 return c1==
static_cast<char>(0xc2) && c2==
static_cast<char>(0xa0);
113 (offset>1 && !
isUtf8Nbsp(data.data()[-2],data.data()[-1])));
120 return c==
'(' || c==
'{' || c==
'[' || (c==
'<' && cn!=
'/') || c==
'\\' || c==
'@';
151 int processLink(std::string_view data,
size_t offset);
159 int isHeaderline(std::string_view data,
bool allowAdjustLevel);
161 bool *pIsIdGenerated=
nullptr);
164 size_t blockStart,
size_t blockEnd);
193 a[
static_cast<unsigned int>(
'[')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
194 a[
static_cast<unsigned int>(
'!')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
204 :
prv(std::make_unique<
Private>(fileName,lineNr,indentLevel))
206 using namespace std::placeholders;
230 if (data[0] ==
'\n')
return 1;
234 return (data.size()>8 && data[8]==
' ') ? 9 : 8;
245 const char *p=s.
data();
249 if (c==
'"' && pc!=
'\\') result+=
'\\';
262 bool insideQuote=
FALSE;
264 const char *p=s.
data();
279 insideQuote=!insideQuote;
291 if ((p[0]==
':') && (p[1]==
':'))
303 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
304 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
307 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
308 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
309 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
324 if (leftMarker && rightMarker)
332 else if (rightMarker)
347 for (
const auto &attr_ : attrList)
350 int i = attr.
find(
':');
357 return attr.
mid(i+1);
395 using EndBlockFunc =
QCString (*)(
const std::string &,bool,char);
397 static constexpr auto getEndBlock = [](
const std::string &blockName,bool,char) ->
QCString
399 return "end"+blockName;
401 static constexpr auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
QCString
403 return openBracket ?
QCString(
"}") :
"end"+blockName;
405 static constexpr auto getEndUml = [](
const std::string &,bool,char) ->
QCString
409 static constexpr auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
QCString
413 case '$':
return "f$";
414 case '(':
return "f)";
415 case '[':
return "f]";
416 case '{':
return "f}";
422 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
424 {
"dot", getEndBlock },
425 {
"code", getEndCode },
426 {
"icode", getEndBlock },
427 {
"msc", getEndBlock },
428 {
"verbatim", getEndBlock },
429 {
"iverbatim", getEndBlock },
430 {
"iliteral", getEndBlock },
431 {
"latexonly", getEndBlock },
432 {
"htmlonly", getEndBlock },
433 {
"xmlonly", getEndBlock },
434 {
"rtfonly", getEndBlock },
435 {
"manonly", getEndBlock },
436 {
"docbookonly", getEndBlock },
437 {
"startuml", getEndUml },
438 {
"mermaid", getEndBlock },
439 {
"f", getEndFormula }
442 const size_t size = data.
size();
443 bool openBracket = offset>0 && data.data()[-1]==
'{';
444 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
445 if (isEscaped)
return result;
448 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
449 if (
end==1)
return result;
450 std::string blockName(data.substr(1,
end-1));
451 auto it = blockNames.find(blockName);
452 if (it!=blockNames.end())
454 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
464 using EndCmdFunc = size_t (*)(std::string_view,size_t);
466 static constexpr auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
471 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
473 if (c==
'\\') lc=
'\\';
474 else if (c!=
' ') lc=0;
480 static constexpr auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
482 if (offset_<data_.size() && data_[offset_]==
' ')
490 while (offset_<data_.size() && data_[offset_]==
' ')
495 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
500 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
502 size_t off = offset_;
503 while (off<data_.size() && data_[off]==
' ')
507 if (off<data_.size() && data_[off]==
',')
526 static constexpr auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
528 return endOfLabels(data_,offset_,
false);
531 static constexpr auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
533 size_t index=offset_;
534 if (index<data_.size() && data_[index]==
' ')
537 while (index<data_.size() && data_[index]==
' ') index++;
539 if (index<data_.size() && data_[index]==
'{')
543 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
544 if (index==data_.size() || data_[index]!=
'}')
return 0;
547 return endOfLabel(data_,offset_);
550 static constexpr auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
552 size_t index=offset_;
553 if (index<data_.size() && data_[index]==
' ')
556 while (index<data_.size() && data_[index]==
' ') index++;
558 if (index<data_.size() && data_[index]==
'[')
562 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
563 if (index==data_.size() || data_[index]!=
']')
return 0;
566 return endOfLabels(data_,offset_,
true);
569 static constexpr auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
571 return endOfLabels(data_,offset_,
true);
574 static constexpr auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
576 if (offset_<data_.size() && data_[offset_]==
' ')
581 while (offset_<data_.size() && data_[offset_]==
' ')
586 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
588 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
595 while (offset_<data_.size() && (c=data_[offset_++]))
598 else if (c==
')') count--;
599 if (count==0)
return offset_;
607 static constexpr auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
609 return endOfFuncLike(data_,offset_,
true);
612 static constexpr auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
614 return endOfFuncLike(data_,offset_,
false);
617 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
620 {
"addindex", endOfLine },
621 {
"addtogroup", endOfLabel },
622 {
"anchor", endOfLabel },
625 {
"category", endOfLine },
626 {
"cite", endOfLabel },
627 {
"class", endOfLine },
628 {
"concept", endOfLine },
629 {
"copybrief", endOfFunc },
630 {
"copydetails", endOfFunc },
631 {
"copydoc", endOfFunc },
632 {
"def", endOfFunc },
633 {
"defgroup", endOfLabel },
634 {
"diafile", endOfLine },
635 {
"dir", endOfLine },
636 {
"dockbookinclude",endOfLine },
637 {
"dontinclude", endOfLine },
638 {
"dotfile", endOfLine },
640 {
"elseif", endOfGuard },
641 {
"em", endOfLabel },
642 {
"emoji", endOfLabel },
643 {
"enum", endOfLabel },
644 {
"example", endOfLine },
645 {
"exception", endOfLine },
646 {
"extends", endOfLabel },
647 {
"file", endOfLine },
649 {
"headerfile", endOfLine },
650 {
"htmlinclude", endOfLine },
651 {
"ianchor", endOfLabelOpt },
652 {
"idlexcept", endOfLine },
653 {
"if", endOfGuard },
654 {
"ifnot", endOfGuard },
655 {
"image", endOfLine },
656 {
"implements", endOfLine },
657 {
"include", endOfLine },
658 {
"includedoc", endOfLine },
659 {
"includelineno", endOfLine },
660 {
"ingroup", endOfLabel },
661 {
"interface", endOfLine },
662 {
"latexinclude", endOfLine },
663 {
"maninclude", endOfLine },
664 {
"memberof", endOfLabel },
665 {
"mscfile", endOfLine },
666 {
"namespace", endOfLabel },
667 {
"noop", endOfLine },
668 {
"overload", endOfLine },
670 {
"package", endOfLabel },
671 {
"page", endOfLabel },
672 {
"paragraph", endOfLabel },
673 {
"param", endOfParam },
674 {
"property", endOfLine },
675 {
"protocol", endOfLine },
676 {
"qualifier", endOfLine },
677 {
"ref", endOfLabel },
678 {
"refitem", endOfLine },
679 {
"related", endOfLabel },
680 {
"relatedalso", endOfLabel },
681 {
"relates", endOfLabel },
682 {
"relatesalso", endOfLabel },
683 {
"requirement", endOfLabel },
684 {
"retval", endOfRetVal},
685 {
"rtfinclude", endOfLine },
686 {
"section", endOfLabel },
687 {
"skip", endOfLine },
688 {
"skipline", endOfLine },
689 {
"snippet", endOfLine },
690 {
"snippetdoc", endOfLine },
691 {
"snippetlineno", endOfLine },
692 {
"struct", endOfLine },
693 {
"satisfies", endOfLabel },
694 {
"subpage", endOfLabel },
695 {
"subparagraph", endOfLabel },
696 {
"subsubparagraph",endOfLabel },
697 {
"subsection", endOfLabel },
698 {
"subsubsection", endOfLabel },
699 {
"throw", endOfLabel },
700 {
"throws", endOfLabel },
701 {
"tparam", endOfLabel },
702 {
"typedef", endOfLine },
703 {
"plantumlfile", endOfLine },
704 {
"union", endOfLine },
705 {
"until", endOfLine },
706 {
"var", endOfLine },
707 {
"verbinclude", endOfLine },
708 {
"verifies", endOfLabel },
709 {
"weakgroup", endOfLabel },
710 {
"xmlinclude", endOfLine },
711 {
"xrefitem", endOfLabel }
714 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
715 if (isEscaped)
return 0;
717 const size_t size = data.size();
719 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
720 if (
end==1)
return 0;
721 std::string cmdName(data.substr(1,
end-1));
723 auto it = cmdNames.find(cmdName);
724 if (it!=cmdNames.end())
727 result = it->second(data,
end);
740 const size_t size = data.size();
744 while (i<size && data[i]!=c &&
745 data[i]!=
'\\' && data[i]!=
'@' &&
746 !(data[i]==
'/' && data[i-1]==
'<') &&
766 while (i+len<size && data[i+len]==c)
773 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
779 return static_cast<int>(i);
787 while (i < size && data[i] ==
'`')
795 while (i<size && enb<snb)
797 if (data[i]==
'`') enb++;
798 if (snb==1 && data[i]==
'\'')
break;
802 else if (data[i]==
'@' || data[i]==
'\\')
808 size_t l = endBlockName.
length();
811 if ((data[i]==
'\\' || data[i]==
'@') &&
812 data[i-1]!=
'\\' && data[i-1]!=
'@')
822 else if (i+1<size &&
isIdChar(data[i+1]))
831 else if (data[i-1]==
'<' && data[i]==
'/')
835 else if (data[i]==
'\n')
838 while (i<size && data[i]==
' ') i++;
839 if (i>=size || data[i]==
'\n')
857 const size_t size = data.size();
860 if (size>1 && data[0]==c && data[1]==c) { i=1; }
865 if (len==0) {
return 0; }
867 if (i>=size) {
return 0; }
869 if (i+1<size && data[i+1]==c)
874 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
880 return static_cast<int>(i+1);
891 const size_t size = data.size();
901 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
903 if (c ==
'~')
out+=
"<strike>";
904 else out+=
"<strong>";
906 if (c ==
'~')
out+=
"</strike>";
907 else out+=
"</strong>";
909 return static_cast<int>(i+2);
923 const size_t size = data.size();
935 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
940 if (i+2<size && data[i+1]==c && data[i+2]==c)
944 out+=
"</strong></em>";
946 return static_cast<int>(i+3);
948 else if (i+1<size && data[i+1]==c)
959 return static_cast<int>(len - 2);
973 return static_cast<int>(len - 1);
984 const size_t size = data.size();
988 if (i<size && data[i]==
'-')
993 if (i<size && data[i]==
'-')
998 if (i<size && data[i]==
'-')
1002 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
1004 if (count==2 && size > 2 && data[2]==
'>')
1006 if (count==3 && size > 3 && data[3]==
'>')
1008 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1028 const size_t size = data.size();
1031 while (i<size && data[i]!=
'"' && nl<2)
1033 if (data[i]==
'\n') nl++;
1036 if (i<size && data[i]==
'"' && nl<2)
1038 out+=data.substr(0,i+1);
1040 return static_cast<int>(i+1);
1052 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1054 const size_t size = data.size();
1060 while (i < size &&
isIdChar(data[i]))
1065 QCString tagName(data.substr(1,i-1));
1066 if (tagName.
lower()==
"pre")
1068 bool insideStr=
FALSE;
1072 if (!insideStr && c==
'<')
1074 if (data[i+1]==
'/' &&
1075 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1076 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1078 if (doWrite)
out+=data.substr(0,i+6);
1081 return static_cast<int>(i+6);
1084 else if (insideStr && c==
'"')
1086 if (data[i-1]!=
'\\') insideStr=
FALSE;
1099 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1102 if (doWrite)
out+=data.substr(0,i+2);
1104 return static_cast<int>(i+2);
1106 else if (data[i]==
'>')
1109 if (doWrite)
out+=data.substr(0,i+1);
1111 return static_cast<int>(i+1);
1113 else if (data[i]==
' ')
1116 bool insideAttr=
FALSE;
1119 if (!insideAttr && data[i]==
'"')
1123 else if (data[i]==
'"' && data[i-1]!=
'\\')
1127 else if (!insideAttr && data[i]==
'>')
1130 if (doWrite)
out+=data.substr(0,i+1);
1132 return static_cast<int>(i+1);
1152 const size_t size = data.size();
1164 if (size>2 && c!=
'~' && data[1]!=c)
1167 if (data[1]==
' ' || data[1]==
'\n' ||
1175 if (size>3 && data[1]==c && data[2]!=c)
1177 if (data[2]==
' ' || data[2]==
'\n' ||
1185 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1187 if (data[3]==
' ' || data[3]==
'\n' ||
1199 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1204 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1215 out+=link.
mid(fd ? 0 : 5);
1216 if (!explicitTitle && !content.
isEmpty())
1222 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1244 const size_t size = data.size();
1249 bool isImageLink =
FALSE;
1250 bool isImageInline =
FALSE;
1256 if (size<2 || data[1]!=
'[')
1265 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1267 if (data.data()[pos]==
'\n') numNLsNeeded--;
1268 else if (data.data()[pos]!=
' ')
1278 size_t contentStart=i;
1285 if (data[i-1]==
'\\')
1288 else if (data[i]==
'[')
1292 else if (data[i]==
']')
1295 if (level<=0)
break;
1297 else if (data[i]==
'\n')
1300 if (nl>1) {
return 0; }
1306 if (i>=size)
return 0;
1307 size_t contentEnd=i;
1308 content = data.substr(contentStart,contentEnd-contentStart);
1310 if (!isImageLink && content.
isEmpty()) {
return 0; }
1313 bool whiteSpace =
false;
1315 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1316 if (i<size && data[i]==
'\n')
1321 while (i<size && data[i]==
' ') i++;
1323 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1325 bool explicitTitle=
FALSE;
1326 if (i<size && data[i]==
'(')
1329 while (i<size && data[i]==
' ') i++;
1330 bool uriFormat=
false;
1331 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1335 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1341 if (nlConsec>1) {
return 0; }
1343 else if (data[i]==
'(')
1348 else if (data[i]==
')')
1353 else if (data[i]!=
' ')
1364 if (i>=size || data[i]==
'\n') {
return 0; }
1365 link = data.substr(linkStart,i-linkStart);
1368 if (link.
isEmpty()) {
return 0; }
1372 if (data[i]==
'\'' || data[i]==
'"')
1376 size_t titleStart=i;
1382 if (nl>1) {
return 0; }
1385 else if (data[i]==
'\\')
1389 else if (data[i]==c)
1400 size_t titleEnd = i-1;
1402 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1403 if (data[titleEnd]==c)
1405 title = data.substr(titleStart,titleEnd-titleStart);
1409 if (data[i]==
' ')i++;
1410 else if (data[i] ==
')')
break;
1424 else if (i<size && data[i]==
'[')
1430 while (i<size && data[i]!=
']')
1435 if (nl>1) {
return 0; }
1439 if (i>=size) {
return 0; }
1441 link = data.substr(linkStart,i-linkStart);
1453 link = lr_it->second.link;
1454 title = lr_it->second.title;
1464 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1471 link = lr_it->second.link;
1472 title = lr_it->second.title;
1476 else if (content==
"TOC")
1499 while (j<size && data[j]==
' ') { j++; }
1500 if (j<size && data[j]==
'{')
1505 size_t attributesStart=i;
1510 if (data[i-1]==
'\\')
1513 else if (data[i]==
'{')
1517 else if (data[i]==
'}')
1520 if (level<=0)
break;
1522 else if (data[i]==
'\n')
1525 if (nl>1) {
return 0; }
1530 if (i>=size)
return 0;
1531 size_t attributesEnd=i;
1532 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1541 while (pos<size && numNLsNeeded>0)
1543 if (data[pos]==
'\n') numNLsNeeded--;
1544 else if (data[pos]!=
' ')
1559 out+=
"@tableofcontents{html:";
1564 else if (isImageLink)
1568 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1573 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1574 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1575 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1576 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1577 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1599 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1631 if (explicitTitle && !title.
isEmpty())
1641 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1644 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1658 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1671 bool foundNameRef =
false;
1672 if (!content.
isEmpty() && (content.
at(0)==
'#' || content.
at(0)==
'@'))
1675 while (endOfId<content.
length() &&
isId(content.
at(endOfId))) endOfId++;
1682 foundNameRef =
true;
1701 return static_cast<int>(i);
1708 const size_t size = data.size();
1712 while (nb<size && data[nb]==
'`')
1729 if (
end+1<size && data[
end+1]==
'`')
1744 if (
end+1<size && data[
end+1]==
'`')
1757 else if (data[
end]==
'\n')
1768 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1771 out+=data.substr(nb,
end-nb);
1774 return static_cast<int>(
end+1);
1776 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1779 out+=data.substr(nb,
end-nb);
1782 return static_cast<int>(
end+2);
1786 if (data[
end]!=
' ') pc = data[
end];
1790 if (i < nb && end >= size)
1795 out+=data.substr(0,nb);
1796 return static_cast<int>(nb);
1800 while (
end<size && data[
end]==
'`')
1810 QCString codeFragment = data.substr(nb,
end-nb-nb);
1816 return static_cast<int>(
end);
1835 const size_t size = data.size();
1841 size_t l = endBlockName.
length();
1844 if ((data[i]==
'\\' || data[i]==
'@') &&
1845 data[i-1]!=
'\\' && data[i-1]!=
'@')
1852 return static_cast<int>(i+1+l);
1861 out+=data.substr(0,endPos);
1862 return static_cast<int>(endPos);
1864 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1867 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1873 else if (c==
'\\' || c==
'@')
1875 out+=data.substr(0,2);
1879 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1881 out+=data.substr(1,3);
1885 else if (c==
'-' && size>2 && data[2]==
'-')
1887 out+=data.substr(1,2);
1901 const size_t size = data.size();
1907 out+=data.substr(i,
end-i);
1908 if (
end>=size)
break;
1911 int iend = action(*
this,data.substr(i),i);
1929 const size_t size = data.size();
1930 while (i<size && data[i]==
' ') i++;
1931 if (i==size)
return 0;
1936 while (i < size && data[i] ==
'=')
1941 while (i<size && data[i]==
' ') i++;
1942 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1943 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1958 while (i < size && data[i] ==
'-')
1963 while (i<size && data[i]==
' ') i++;
1964 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1974 const size_t size = data.size();
1975 while (i<size && data[i]==
' ') i++;
1980 while (i<size && (data[i]==
'>' || data[i]==
' '))
1982 if (data[i]==
'>') level++;
1987 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
2002 const size_t size = data.size();
2005 while (i<size && data[i]==
' ') i++;
2006 if (i>=size || data[i]!=
'[') {
return 0; }
2008 size_t refIdStart=i;
2009 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
2010 if (i>=size || data[i]!=
']') {
return 0; }
2011 refid = data.substr(refIdStart,i-refIdStart);
2012 if (refid.
isEmpty()) {
return 0; }
2016 if (i>=size || data[i]!=
':') {
return 0; }
2020 while (i<size && data[i]==
' ') i++;
2021 if (i<size && data[i]==
'\n')
2024 while (i<size && data[i]==
' ') i++;
2026 if (i>=size) {
return 0; }
2028 if (i<size && data[i]==
'<') i++;
2030 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2032 if (i<size && data[i]==
'>') i++;
2033 if (linkStart==linkEnd) {
return 0; }
2034 link = data.substr(linkStart,linkEnd-linkStart);
2036 if (link==
"@ref" || link==
"\\ref")
2039 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2040 link+=data.substr(argStart,i-argStart);
2047 while (i<size && data[i]==
' ') i++;
2048 if (i<size && data[i]==
'\n')
2052 while (i<size && data[i]==
' ') i++;
2056 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2061 if (c==
'\'' || c==
'"' || c==
'(')
2066 size_t titleStart=i;
2068 while (i<size && data[i]!=
'\n') i++;
2073 while (
end>titleStart && data[
end]!=c)
end--;
2076 title = data.substr(titleStart,
end-titleStart);
2080 while (i<size && data[i]==
' ') i++;
2092 size_t size = data.size();
2093 if (size>0 && data[size-1]==
'\n') size--;
2094 while (i<size && data[i]==
' ') i++;
2095 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2097 if (c!=
'*' && c!=
'-' && c!=
'_')
2109 else if (data[i]!=
' ')
2124 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2126 std::string ti = title.str();
2129 std::string
id = match[1].str();
2130 title = title.
left(match.position());
2133 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2139 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2142 if (pIsIdGenerated) *pIsIdGenerated=
true;
2157 int level = 0, blanks=0;
2158 const size_t size = data.size();
2161 while (i<size && data[i]==
' ') i++;
2162 if (i>=size || data[i]!=
'#')
2166 while (i < size && data[i] ==
'#')
2175 while (i < size && data[i] ==
' ')
2180 if (level==1 && blanks==0)
2187 while (
end<size && data[
end]!=
'\n')
end++;
2188 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2191 header = data.substr(i,
end-i);
2195 int idx=
static_cast<int>(header.
length())-1;
2196 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2197 header=header.
left(idx+1);
2200 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2231 while (i<data.size())
2242 (data[(i)]=='<' && \
2243 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2244 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2253 const size_t size=data.size();
2257 bool listMarkerSkipped=
FALSE;
2260 (!listMarkerSkipped &&
2261 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2262 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2263 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2264 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2273 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2277 if (j+1<size && data[j+1]==
' ')
2279 listMarkerSkipped=
TRUE;
2296 listMarkerSkipped=
TRUE;
2298 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2300 listMarkerSkipped=
TRUE;
2304 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2306 listMarkerSkipped=
TRUE;
2308 if (data[i]!=
' ' && !listMarkerSkipped)
2322 size_t normalIndent = 0;
2323 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2325 size_t result = listIndent>normalIndent ? listIndent : 0;
2336 while (i<data.size())
2342 else if (data[i]==
'\n')
2346 else if (data[i]!=
' ' && data[i]!=
'\t')
2358 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2362 const char dot =
'.';
2363 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2364 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2365 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2371 const size_t size = data.size();
2372 while (i < size && data[i] ==
' ')
2377 if (indent>=refIndent+4)
2379 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2383 if (i<size && data[i]==
'`') tildaChar=
'`';
2384 while (i < size && data[i] == tildaChar)
2391 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2395 while (i<size && data[i]==
' ') { i++; }
2396 if (i<size && data[i]==
'{')
2399 if (data[i] == dot) i++;
2401 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2402 if (i<size && data[i]==
'}')
2404 lang = data.substr(startLang,i-startLang);
2413 else if (i<size && isLangChar(data[i]))
2415 if (data[i] == dot) i++;
2417 if (i<size && isAlphaChar(data[i]))
2420 while (i<size && isAlphaNChar(data[i])) i++;
2422 lang = data.substr(startLang,i-startLang);
2432 if (data[i]==tildaChar)
2436 while (i < size && data[i] == tildaChar)
2441 while (i<size && data[i]==
' ') i++;
2443 if (endTildes==startTildes)
2446 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2453 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2458static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2465 const size_t size = data.size();
2466 while (i < size && data[i] ==
' ')
2474 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2477 if (indent0>=size || data[indent0]==
'\n')
2479 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2486 int offset_i =
static_cast<int>(offset);
2490 int j =
static_cast<int>(i)-offset_i-1;
2492 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2495 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2501 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2512 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2521 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2527 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2534 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2542 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2558 const size_t size = data.size();
2561 while (i<size && data[i]==
' ') i++;
2562 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2571 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2574 if (j>0 && i>0) i--;
2575 while (i>0 && data[i]==
' ') i--;
2576 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2590 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2591 if (columns==1) columns++;
2595 if (n==2 && columns==0)
2607 size_t cc0=0, start=0,
end=0;
2611 if (i>=data.size() || cc0<1)
2623 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2632 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2647 const size_t size = data.size();
2649 size_t columns=0, start=0,
end=0;
2651 size_t headerStart = start;
2652 size_t headerEnd =
end;
2658 std::vector<Alignment> columnAlignment(columns);
2660 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2666 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2667 if (data[j]==
'-') startFound=
TRUE;
2670 if (data[j]==
'-') rightMarker=
FALSE;
2671 else if (data[j]==
':') rightMarker=
TRUE;
2672 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2696 std::vector<std::vector<TableCell> > tableContents;
2698 size_t m = headerStart;
2699 std::vector<TableCell> headerContents(columns);
2700 for (k=0;k<columns;k++)
2702 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2704 headerContents[k].cellText += data[m++];
2709 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2710 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2712 tableContents.push_back(headerContents);
2718 if (cc!=columns)
break;
2722 std::vector<TableCell> rowContents(columns);
2725 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2729 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2730 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2735 rowContents[k].cellText += data[j];
2741 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2742 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2743 tableContents.push_back(rowContents);
2749 out+=
"<table class=\"markdownTable\">";
2750 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2751 for (
size_t row = 0; row < tableContents.size(); row++)
2757 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2761 out+=
"\n<tr class=\"markdownTableRowEven\">";
2766 out+=
"\n <tr class=\"markdownTableHead\">";
2768 for (
size_t c = 0; c < columns; c++)
2771 QCString cellText(tableContents[row][c].cellText);
2777 if (tableContents[row][c].cellText ==
"^")
2781 if (tableContents[row][c].colSpan)
2783 int cr =
static_cast<int>(c);
2784 while ( cr >= 0 && tableContents[row][cr].colSpan)
2788 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2790 size_t rowSpan = 1, spanRow = row+1;
2791 while ((spanRow < tableContents.size()) &&
2792 (tableContents[spanRow][c].cellText ==
"^"))
2798 out+=
" <" + cellTag +
" " + cellClass;
2800 switch (columnAlignment[c])
2812 out+=
" rowspan=\"" + spanStr +
"\"";
2818 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2827 out+=
" colspan=\"" + spanStr +
"\"";
2831 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2834 cellClass =
"class=\"markdownTableBody";
2850 while (i<data.size() && data[i]!=
'\n')
2852 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2855 if (i>=data.size()) {
return 0; }
2856 if (i<2) {
return 0; }
2857 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2897 out+=
"</"+hTag+
">\n";
2900 else if (data.size()>0)
2902 size_t tmpSize = data.size();
2903 if (data[data.size()-1] ==
'\n') tmpSize--;
2904 out+=data.substr(0,tmpSize);
2908 out+=
"\\ilinebr<br>";
2910 if (tmpSize != data.size())
out+=
'\n';
2916 {
"[!note]",
"\\note" },
2917 {
"[!warning]",
"\\warning" },
2918 {
"[!tip]",
"\\remark" },
2919 {
"[!caution]",
"\\attention" },
2920 {
"[!important]",
"\\important" }
2929 const size_t size = data.size();
2930 std::string startCmd;
2931 int isGitHubAlert =
false;
2932 int isGitHubFirst =
false;
2937 while (
end<=size && data[
end-1]!=
'\n')
end++;
2942 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2944 if (data[j]==
'>') { level++; indent=j+1; }
2945 else if (j>0 && data[j-1]==
'>') indent=j+1;
2948 if (indent>0 && j>0 && data[j-1]==
'>' &&
2949 !(j==size || data[j]==
'\n'))
2966 isGitHubAlert =
true;
2967 isGitHubFirst =
true;
2968 startCmd = it->second;
2973 if (level!=1 || !isGitHubAlert)
2975 for (
int l=curLevel;l<level-1;l++)
2977 out+=
"<blockquote>";
2979 out +=
"<blockquote>‍";
2981 else if (!startCmd.empty())
2983 out += startCmd +
" ";
2986 else if (level<curLevel)
2989 if (level==0 && isGitHubAlert)
2995 out +=
"</blockquote>\\ilinebr ";
3004 if (curLevel!=0 || !isGitHubAlert)
3006 std::string_view txt = data.substr(indent,
end-indent);
3009 if (!isGitHubFirst)
out +=
"<br>";
3016 isGitHubFirst =
false;
3031 for (
int l=0;l<curLevel;l++)
3033 out+=
"</blockquote>";
3044 size_t size = data.size();
3045 while (i<data.size() && data[i]==
' ') i++;
3048 size_t locStart = i;
3049 if (i>offset) locStart--;
3052 while (i+9<size && data[i]!=
'\n')
3064 location=data.substr(locStart,i-locStart);
3066 while (indent > 0 && i < size && data[i] ==
' ')
3071 if (i<size && data[i]==
'\n') i++;
3082 const size_t size = data.size();
3085 out+=
"@iverbatim\n";
3087 std::string location;
3092 while (
end<=size && data[
end-1]!=
'\n')
end++;
3095 while (j <
end && data[j] ==
' ')
3109 while (emptyLines>0)
3117 std::string lineLoc;
3122 out+=data.substr(offset,
end-offset);
3130 out+=
"@endiverbatim";
3131 if (!location.empty())
3139 while (emptyLines>0)
3155 const size_t size = data.size();
3156 size_t nb=0,
end=offset+1, j=0;
3161 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3162 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3169 size_t l = endBlockName.
length();
3170 for (;
end+l+1<size;
end++)
3172 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3173 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3187 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3188 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3191 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3192 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3203 else if (nb==0 && data[
end-1]==
'`')
3205 while (
end <= size && data[
end - 1] ==
'`')
3211 else if (nb>0 && data[
end-1]==
'`')
3214 while (
end <= size && data[
end - 1] ==
'`')
3232 size_t blockStart,
size_t blockEnd)
3235 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3236 const size_t size=data.size();
3238 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3259 size_t pi=std::string::npos;
3260 bool newBlock =
false;
3261 bool insideList =
false;
3262 size_t currentIndent = refIndent;
3263 size_t listIndent = refIndent;
3264 const size_t size = data.size();
3271 size_t lineIndent=0;
3272 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3278 if (insideList && lineIndent<currentIndent)
3281 currentIndent = refIndent;
3289 if (listIndent<currentIndent+4)
3293 currentIndent = listIndent;
3300 currentIndent = listIndent;
3309 if (pi!=std::string::npos)
3311 size_t blockStart=0, blockEnd=0, blockOffset=0;
3314 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3316 size_t cmdPos = pi+blockStart+1;
3317 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3323 if (pl[ii]==
'\n') nl++;
3326 bool addNewLines =
false;
3328 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3337 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3338 addNewLines =
false;
3353 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3355 if (addNewLines)
out+=
'\n';
3360 addSpecialCommand(
"startuml",
"enduml");
3364 addSpecialCommand(
"dot",
"enddot");
3366 else if (lang==
"msc")
3368 addSpecialCommand(
"msc",
"endmsc");
3370 else if (lang==
"mermaid")
3372 addSpecialCommand(
"mermaid",
"endmermaid");
3379 pi=std::string::npos;
3383 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3386 pi=std::string::npos;
3393 out+=data.substr(pi,i-pi);
3399 if (pi!=std::string::npos && pi<size)
3409 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3411 out+=data.substr(pi);
3425 size_t pi = std::string::npos;
3441 size_t currentIndent = indent;
3442 size_t listIndent = indent;
3443 bool insideList =
false;
3444 bool newBlock =
false;
3447 while (i<data.size())
3452 size_t lineIndent=0;
3454 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3460 if (insideList && lineIndent<currentIndent)
3463 currentIndent = indent;
3471 if (listIndent<currentIndent+4)
3475 currentIndent = listIndent;
3482 currentIndent = listIndent;
3494 if (pi!=std::string::npos)
3496 size_t blockStart=0, blockEnd=0, blockOffset=0;
3498 size_t blockIndent = currentIndent;
3502 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3506 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3516 size_t l = endBlockName.
length();
3517 while (i+l<data.size())
3519 if ((data[i]==
'\\' || data[i]==
'@') &&
3520 data[i-1]!=
'\\' && data[i-1]!=
'@')
3537 while (pi<data.size() && data[pi]==
' ') pi++;
3538 QCString header = data.substr(pi,i-pi-1);
3545 out+=level==1?
"@section ":
"@subsection ";
3553 out+=level==1?
"<h1>":
"<h2>";
3555 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3562 pi=std::string::npos;
3567 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3581 pi=std::string::npos;
3589 pi=std::string::npos;
3596 pi=std::string::npos;
3609 if (pi!=std::string::npos && pi<data.size())
3611 if (
isLinkRef(data.substr(pi),
id,link,title))
3628#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3629 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3632 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3633 OPC(idlexcept);
OPC(file);
3643 std::string_view data(docs.
str());
3644 const size_t size = data.size();
3647 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3654 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3660 (data[i]==
'\\' || data[i]==
'@') &&
3675 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3693 std::string_view data(docs_org.
str());
3694 const size_t size = data.size();
3696 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3698 if (data[i]==
'\n') prepend++;
3701 if (i>=size) {
return QCString(); }
3703 while (end1<size && data[end1-1]!=
'\n') end1++;
3710 while (end2<size && data[end2-1]!=
'\n') end2++;
3711 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3713 title = data.substr(i,end1-i-1);
3714 docs+=
"\n\n"+docs_org.
mid(end2);
3715 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3721 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3724 docs+=docs_org.
mid(end1);
3729 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3740 if (input.
isEmpty())
return input;
3745 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3746 s =
detab(s,refIndent);
3750 s =
prv->processQuotations(s.
view(),refIndent);
3754 s =
prv->processBlocks(s.
view(),refIndent);
3772 const char *p = result.
data();
3775 while (*p==
' ') p++;
3776 while (*p==
'\n') {startNewlines++;p++;};
3779 if (p>result.
data())
3782 result = result.
mid(
static_cast<int>(p-result.
data()));
3795 if (i!=-1) baseFn = baseFn.
left(i);
3819 const char *fileBuf,
3820 const std::shared_ptr<Entry> &root,
3823 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3825 current->lang = SrcLangExt::Markdown;
3826 current->fileName = fileName;
3827 current->docFile = fileName;
3828 current->docLine = 1;
3834 bool isIdGenerated =
false;
3842 int indentLevel=title.
isEmpty() ? 0 : -1;
3849 bool wasEmpty =
id.isEmpty();
3850 if (wasEmpty)
id = mdFileNameId;
3856 if (!mdfileAsMainPage.
isEmpty() &&
3860 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3861 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3863 else if (
id==
"mainpage" ||
id==
"index")
3865 if (title.
isEmpty()) title = titleFn;
3866 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3867 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3869 else if (isSubdirDocs)
3873 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3875 docs.
prepend(
"@dir\\ilinebr ");
3886 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3888 else if (!generatedId.
isEmpty())
3890 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3892 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3895 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3897 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3899 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3904 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3906 std::string s = docs.str();
3909 QCString orgLabel = match[1].str();
3910 QCString orgTitle = match[2].str();
3913 docs = docs.
left(match[1].position())+
3916 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3928 p->commentScanner.enterFile(fileName,lineNr);
3930 bool needsEntry =
false;
3934 while (
p->commentScanner.parseCommentBlock(
3952 QCString docFile = current->docFile;
3953 root->moveToSubEntryAndRefresh(current);
3954 current->lang = SrcLangExt::Markdown;
3955 current->docFile = docFile;
3956 current->docLine = lineNr;
3961 root->moveToSubEntryAndKeep(current);
3963 p->commentScanner.leaveFile(fileName,lineNr);
#define eol
The end of line string for this machine.
static AnchorGenerator & instance()
Returns the singleton instance.
static std::string addPrefixIfNeeded(const std::string &anchor)
std::string generate(const std::string &title)
generates an anchor for a section with title.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
static ParserManager * parserManager
static FileNameLinkedMap * imageNameLinkedMap
A model of a file symbol.
Minimal replacement for QFileInfo.
std::string fileName() const
std::string absFilePath() const
Helper class to process markdown formatted text.
static ActionTable_t fill_table()
std::array< Action_t, 256 > ActionTable_t
std::unique_ptr< Private > prv
static ActionTable_t actions
void setIndentLevel(int level)
QCString extractPageTitle(QCString &docs, QCString &id, int &prepend, bool &isIdGenerated)
Markdown(const QCString &fileName, int lineNr, int indentLevel=0)
QCString process(const QCString &input, int &startNewlines, bool fromParseInput=false)
std::function< int(Private &, std::string_view, size_t)> Action_t
void parseInput(const QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser) override
Parses a single input file with the goal to build an Entry tree.
~MarkdownOutlineParser() override
void parsePrototype(const QCString &text) override
Callback function called by the comment block scanner.
std::unique_ptr< Private > p
This is an alternative implementation of QCString.
int find(char c, int index=0, bool cs=TRUE) const
QCString & prepend(const char *s)
size_t length() const
Returns the length of the string, not counting the 0-terminator.
bool startsWith(const char *s) const
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
bool endsWith(const char *s) const
char & at(size_t i)
Returns a reference to the character at index i.
bool isEmpty() const
Returns TRUE iff the string is empty.
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
const std::string & str() const
QCString & setNum(short n)
QCString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple whitespace cha...
QCString right(size_t len) const
size_t size() const
Returns the length of the string, not counting the 0-terminator.
QCString & sprintf(const char *format,...)
int findRev(char c, int index=-1, bool cs=TRUE) const
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
std::string_view view() const
QCString left(size_t len) const
static constexpr int Section
static constexpr int MaxLevel
static constexpr int Subsection
static constexpr int Subsubsection
static constexpr int MinLevel
static constexpr int Paragraph
static constexpr int Subsubparagraph
static constexpr int Subparagraph
Class representing a regular expression.
Object representing the matching results.
#define Config_getInt(name)
#define Config_getBool(name)
#define Config_getString(name)
#define Config_getEnum(name)
std::vector< std::string > StringVector
DirIterator end(const DirIterator &) noexcept
#define AUTO_TRACE_ADD(...)
#define AUTO_TRACE_EXIT(...)
static bool isOtherPage(std::string_view data)
static constexpr bool isAllowedEmphStr(const std::string_view &data, size_t offset)
static bool hasLineBreak(std::string_view data)
@ explicitMainPage
docs start with a mainpage command
@ explicitPage
docs start with a page command
@ notExplicit
docs doesn't start with either page or mainpage
@ explicitOtherPage
docs start with a dir / defgroup / addtogroup command
static bool isBlockQuote(std::string_view data, size_t indent)
returns true if this line starts a block quote
static size_t isLinkRef(std::string_view data, QCString &refid, QCString &link, QCString &title)
returns end of the link ref if this is indeed a link reference.
static QCString escapeDoubleQuotes(const QCString &s)
static constexpr Alignment markersToAlignment(bool leftMarker, bool rightMarker)
helper function to convert presence of left and/or right alignment markers to an alignment value
static bool isEndOfList(std::string_view data)
static size_t computeIndentExcludingListMarkers(std::string_view data)
static const char * g_doxy_nbsp
static QCString escapeSpecialChars(const QCString &s)
static constexpr bool ignoreCloseEmphChar(char c, char cn)
static constexpr bool isOpenEmphChar(char c)
static bool isCodeBlock(std::string_view data, size_t offset, size_t &indent)
static bool isEmptyLine(std::string_view data)
#define AUTO_TRACE_EXIT(...)
static size_t findTableColumns(std::string_view data, size_t &start, size_t &end, size_t &columns)
Finds the location of the table's contains in the string data.
static const size_t codeBlockIndent
static constexpr bool isIdChar(char c)
static ExplicitPageResult isExplicitPage(const QCString &docs)
static bool isFencedCodeBlock(std::string_view data, size_t refIndent, QCString &lang, size_t &start, size_t &end, size_t &offset, QCString &fileName, int lineNr)
static const char * g_utf8_nbsp
static const std::unordered_map< std::string, std::string > g_quotationHeaderMap
static size_t isListMarker(std::string_view data)
static bool isHRuler(std::string_view data)
static QCString getFilteredImageAttributes(std::string_view fmt, const QCString &attrs)
parse the image attributes and return attributes for given format
bool skipOverFileAndLineCommands(std::string_view data, size_t indent, size_t &offset, std::string &location)
static constexpr bool extraChar(char c)
static bool isTableBlock(std::string_view data)
Returns TRUE iff data points to the start of a table block.
static constexpr bool isUtf8Nbsp(char c1, char c2)
size_t isNewline(std::string_view data)
QCString markdownFileNameToId(const QCString &fileName)
processes string s and converts markdown into doxygen/html commands.
#define warn(file, line, fmt,...)
const Mapper< CommandType > * cmdMapper
bool isAbsolutePath(const QCString &fileName)
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
QCString trunc(const QCString &s, size_t numChars=15)
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Portable versions of functions that are platform dependent.
static void decrLevel(yyscan_t yyscanner)
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
int qstrncmp(const char *str1, const char *str2, size_t len)
const char * qPrint(const char *s)
Some helper functions for std::string.
bool literal_at(const char *data, const char(&str)[N])
returns TRUE iff data points to a substring that matches string literal str
std::string_view stripWhiteSpace(std::string_view s)
Given a string view s, returns a new, narrower view on that string, skipping over any leading or trai...
LinkRef(const QCString &l, const QCString &t)
int processEmphasis1(std::string_view data, char c)
process single emphasis
int processQuoted(std::string_view data, size_t offset)
Process quoted section "...", can contain one embedded newline.
void writeMarkdownImage(std::string_view fmt, bool inline_img, bool explicitTitle, const QCString &title, const QCString &content, const QCString &link, const QCString &attributes, const FileDef *fd)
size_t writeTableBlock(std::string_view data)
size_t writeBlockQuote(std::string_view data)
size_t isSpecialCommand(std::string_view data, size_t offset)
int processEmphasis3(std::string_view data, char c)
Parsing triple emphasis.
int processCodeSpan(std::string_view data, size_t offset)
` parsing a code span (assuming codespan != 0)
int processSpecialCommand(std::string_view data, size_t offset)
QCString extractTitleId(QCString &title, int level, bool *pIsIdGenerated=nullptr)
void writeFencedCodeBlock(std::string_view data, std::string_view lang, size_t blockStart, size_t blockEnd)
int isHeaderline(std::string_view data, bool allowAdjustLevel)
returns whether the line is a setext-style hdr underline
size_t findEmphasisChar(std::string_view, char c, size_t c_size)
looks for the next emph char, skipping other constructs, and stopping when either it is found,...
std::unordered_map< std::string, LinkRef > linkRefs
void addStrEscapeUtf8Nbsp(std::string_view data)
QCString isBlockCommand(std::string_view data, size_t offset)
size_t writeCodeBlock(std::string_view, size_t refIndent)
int processHtmlTag(std::string_view data, size_t offset)
QCString processQuotations(std::string_view data, size_t refIndent)
QCString processBlocks(std::string_view data, size_t indent)
int processEmphasis(std::string_view data, size_t offset)
int processLink(std::string_view data, size_t offset)
int processHtmlTagWrite(std::string_view data, size_t offset, bool doWrite)
Process a HTML tag.
int isAtxHeader(std::string_view data, QCString &header, QCString &id, bool allowAdjustLevel, bool *pIsIdGenerated=nullptr)
size_t findEndOfLine(std::string_view data, size_t offset)
int processEmphasis2(std::string_view data, char c)
process double emphasis
void processInline(std::string_view data)
Private(const QCString &fn, int line, int indent)
int processNmdash(std::string_view data, size_t offset)
Process ndash and mdashes.
void writeOneLineHeaderOrRuler(std::string_view data)
CommentScanner commentScanner
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
QCString stripIndentation(const QCString &s, bool skipFirstLine)
QCString escapeCharsInString(const QCString &name, bool allowDots, bool allowUnderscore)
QCString stripExtensionGeneral(const QCString &fName, const QCString &ext)
bool isURL(const QCString &url)
Checks whether the given url starts with a supported protocol.
static QCString stripFromPath(const QCString &p, const StringVector &l)
QCString detab(const QCString &s, size_t &refIndent)
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
QCString externalLinkTarget(const bool parent)
QCString getFileNameExtension(const QCString &fn)
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
A bunch of utility functions.