36#include <unordered_map>
57#if !ENABLE_MARKDOWN_TRACING
61#define AUTO_TRACE(...) (void)0
62#define AUTO_TRACE_ADD(...) (void)0
63#define AUTO_TRACE_EXIT(...) (void)0
79 return (c>=
'a' && c<=
'z') ||
82 (
static_cast<unsigned char>(c)>=0x80);
88 return c==
'-' || c==
'+' || c==
'!' || c==
'?' || c==
'$' || c==
'@' ||
89 c==
'&' || c==
'*' || c==
'_' || c==
'%' || c==
'[' || c==
'(' ||
90 c==
'.' || c==
'>' || c==
':' || c==
',' || c==
';' || c==
'\'' ||
91 c==
'"' || c==
'`' || c==
'\\';
97 return c==
'\n' || c==
' ' || c==
'\'' || c==
'<' ||
98 c==
'>' || c==
'{' || c==
'(' || c==
'[' ||
99 c==
',' || c==
':' || c==
';';
105 return c1==
static_cast<char>(0xc2) && c2==
static_cast<char>(0xa0);
111 (offset>1 && !
isUtf8Nbsp(data.data()[-2],data.data()[-1])));
118 return c==
'(' || c==
'{' || c==
'[' || (c==
'<' && cn!=
'/') || c==
'\\' || c==
'@';
149 int processLink(std::string_view data,
size_t offset);
157 int isHeaderline(std::string_view data,
bool allowAdjustLevel);
159 bool *pIsIdGenerated=
nullptr);
162 size_t blockStart,
size_t blockEnd);
191 a[
static_cast<unsigned int>(
'[')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
192 a[
static_cast<unsigned int>(
'!')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
202 :
prv(std::make_unique<
Private>(fileName,lineNr,indentLevel))
204 using namespace std::placeholders;
228 if (data[0] ==
'\n')
return 1;
232 return (data.size()>8 && data[8]==
' ') ? 9 : 8;
243 const char *p=s.
data();
247 if (c==
'"' && pc!=
'\\') result+=
'\\';
260 bool insideQuote=
FALSE;
262 const char *p=s.
data();
277 insideQuote=!insideQuote;
289 if ((p[0]==
':') && (p[1]==
':'))
301 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
302 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
305 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
306 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
307 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
322 if (leftMarker && rightMarker)
330 else if (rightMarker)
345 for (
const auto &attr_ : attrList)
348 int i = attr.
find(
':');
355 return attr.
mid(i+1);
393 using EndBlockFunc =
QCString (*)(
const std::string &,bool,char);
395 static constexpr auto getEndBlock = [](
const std::string &blockName,bool,char) ->
QCString
397 return "end"+blockName;
399 static constexpr auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
QCString
401 return openBracket ?
QCString(
"}") :
"end"+blockName;
403 static constexpr auto getEndUml = [](
const std::string &,bool,char) ->
QCString
407 static constexpr auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
QCString
411 case '$':
return "f$";
412 case '(':
return "f)";
413 case '[':
return "f]";
414 case '{':
return "f}";
420 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
422 {
"dot", getEndBlock },
423 {
"code", getEndCode },
424 {
"icode", getEndBlock },
425 {
"msc", getEndBlock },
426 {
"verbatim", getEndBlock },
427 {
"iverbatim", getEndBlock },
428 {
"iliteral", getEndBlock },
429 {
"latexonly", getEndBlock },
430 {
"htmlonly", getEndBlock },
431 {
"xmlonly", getEndBlock },
432 {
"rtfonly", getEndBlock },
433 {
"manonly", getEndBlock },
434 {
"docbookonly", getEndBlock },
435 {
"startuml", getEndUml },
436 {
"mermaid", getEndBlock },
437 {
"f", getEndFormula }
440 const size_t size = data.
size();
441 bool openBracket = offset>0 && data.data()[-1]==
'{';
442 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
443 if (isEscaped)
return result;
446 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
447 if (
end==1)
return result;
448 std::string blockName(data.substr(1,
end-1));
449 auto it = blockNames.find(blockName);
450 if (it!=blockNames.end())
452 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
462 using EndCmdFunc = size_t (*)(std::string_view,size_t);
464 static constexpr auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
469 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
471 if (c==
'\\') lc=
'\\';
472 else if (c!=
' ') lc=0;
478 static constexpr auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
480 if (offset_<data_.size() && data_[offset_]==
' ')
488 while (offset_<data_.size() && data_[offset_]==
' ')
493 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
498 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
500 size_t off = offset_;
501 while (off<data_.size() && data_[off]==
' ')
505 if (off<data_.size() && data_[off]==
',')
524 static constexpr auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
526 return endOfLabels(data_,offset_,
false);
529 static constexpr auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
531 size_t index=offset_;
532 if (index<data_.size() && data_[index]==
' ')
535 while (index<data_.size() && data_[index]==
' ') index++;
537 if (index<data_.size() && data_[index]==
'{')
541 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
542 if (index==data_.size() || data_[index]!=
'}')
return 0;
545 return endOfLabel(data_,offset_);
548 static constexpr auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
550 size_t index=offset_;
551 if (index<data_.size() && data_[index]==
' ')
554 while (index<data_.size() && data_[index]==
' ') index++;
556 if (index<data_.size() && data_[index]==
'[')
560 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
561 if (index==data_.size() || data_[index]!=
']')
return 0;
564 return endOfLabels(data_,offset_,
true);
567 static constexpr auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
569 return endOfLabels(data_,offset_,
true);
572 static constexpr auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
574 if (offset_<data_.size() && data_[offset_]==
' ')
579 while (offset_<data_.size() && data_[offset_]==
' ')
584 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
586 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
593 while (offset_<data_.size() && (c=data_[offset_++]))
596 else if (c==
')') count--;
597 if (count==0)
return offset_;
605 static constexpr auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
607 return endOfFuncLike(data_,offset_,
true);
610 static constexpr auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
612 return endOfFuncLike(data_,offset_,
false);
615 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
618 {
"addindex", endOfLine },
619 {
"addtogroup", endOfLabel },
620 {
"anchor", endOfLabel },
623 {
"category", endOfLine },
624 {
"cite", endOfLabel },
625 {
"class", endOfLine },
626 {
"concept", endOfLine },
627 {
"copybrief", endOfFunc },
628 {
"copydetails", endOfFunc },
629 {
"copydoc", endOfFunc },
630 {
"def", endOfFunc },
631 {
"defgroup", endOfLabel },
632 {
"diafile", endOfLine },
633 {
"dir", endOfLine },
634 {
"dockbookinclude",endOfLine },
635 {
"dontinclude", endOfLine },
636 {
"dotfile", endOfLine },
638 {
"elseif", endOfGuard },
639 {
"em", endOfLabel },
640 {
"emoji", endOfLabel },
641 {
"enum", endOfLabel },
642 {
"example", endOfLine },
643 {
"exception", endOfLine },
644 {
"extends", endOfLabel },
645 {
"file", endOfLine },
647 {
"headerfile", endOfLine },
648 {
"htmlinclude", endOfLine },
649 {
"ianchor", endOfLabelOpt },
650 {
"idlexcept", endOfLine },
651 {
"if", endOfGuard },
652 {
"ifnot", endOfGuard },
653 {
"image", endOfLine },
654 {
"implements", endOfLine },
655 {
"include", endOfLine },
656 {
"includedoc", endOfLine },
657 {
"includelineno", endOfLine },
658 {
"ingroup", endOfLabel },
659 {
"interface", endOfLine },
660 {
"latexinclude", endOfLine },
661 {
"maninclude", endOfLine },
662 {
"memberof", endOfLabel },
663 {
"mscfile", endOfLine },
664 {
"namespace", endOfLabel },
665 {
"noop", endOfLine },
666 {
"overload", endOfLine },
668 {
"package", endOfLabel },
669 {
"page", endOfLabel },
670 {
"paragraph", endOfLabel },
671 {
"param", endOfParam },
672 {
"property", endOfLine },
673 {
"protocol", endOfLine },
674 {
"qualifier", endOfLine },
675 {
"ref", endOfLabel },
676 {
"refitem", endOfLine },
677 {
"related", endOfLabel },
678 {
"relatedalso", endOfLabel },
679 {
"relates", endOfLabel },
680 {
"relatesalso", endOfLabel },
681 {
"requirement", endOfLabel },
682 {
"retval", endOfRetVal},
683 {
"rtfinclude", endOfLine },
684 {
"section", endOfLabel },
685 {
"skip", endOfLine },
686 {
"skipline", endOfLine },
687 {
"snippet", endOfLine },
688 {
"snippetdoc", endOfLine },
689 {
"snippetlineno", endOfLine },
690 {
"struct", endOfLine },
691 {
"satisfies", endOfLabel },
692 {
"subpage", endOfLabel },
693 {
"subparagraph", endOfLabel },
694 {
"subsubparagraph",endOfLabel },
695 {
"subsection", endOfLabel },
696 {
"subsubsection", endOfLabel },
697 {
"throw", endOfLabel },
698 {
"throws", endOfLabel },
699 {
"tparam", endOfLabel },
700 {
"typedef", endOfLine },
701 {
"plantumlfile", endOfLine },
702 {
"union", endOfLine },
703 {
"until", endOfLine },
704 {
"var", endOfLine },
705 {
"verbinclude", endOfLine },
706 {
"verifies", endOfLabel },
707 {
"weakgroup", endOfLabel },
708 {
"xmlinclude", endOfLine },
709 {
"xrefitem", endOfLabel }
712 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
713 if (isEscaped)
return 0;
715 const size_t size = data.size();
717 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
718 if (
end==1)
return 0;
719 std::string cmdName(data.substr(1,
end-1));
721 auto it = cmdNames.find(cmdName);
722 if (it!=cmdNames.end())
725 result = it->second(data,
end);
738 const size_t size = data.size();
742 while (i<size && data[i]!=c &&
743 data[i]!=
'\\' && data[i]!=
'@' &&
744 !(data[i]==
'/' && data[i-1]==
'<') &&
764 while (i+len<size && data[i+len]==c)
771 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
777 return static_cast<int>(i);
785 while (i < size && data[i] ==
'`')
793 while (i<size && enb<snb)
795 if (data[i]==
'`') enb++;
796 if (snb==1 && data[i]==
'\'')
break;
800 else if (data[i]==
'@' || data[i]==
'\\')
806 size_t l = endBlockName.
length();
809 if ((data[i]==
'\\' || data[i]==
'@') &&
810 data[i-1]!=
'\\' && data[i-1]!=
'@')
820 else if (i+1<size &&
isIdChar(data[i+1]))
829 else if (data[i-1]==
'<' && data[i]==
'/')
833 else if (data[i]==
'\n')
836 while (i<size && data[i]==
' ') i++;
837 if (i>=size || data[i]==
'\n')
855 const size_t size = data.size();
858 if (size>1 && data[0]==c && data[1]==c) { i=1; }
863 if (len==0) {
return 0; }
865 if (i>=size) {
return 0; }
867 if (i+1<size && data[i+1]==c)
872 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
878 return static_cast<int>(i+1);
889 const size_t size = data.size();
899 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
901 if (c ==
'~')
out+=
"<strike>";
902 else out+=
"<strong>";
904 if (c ==
'~')
out+=
"</strike>";
905 else out+=
"</strong>";
907 return static_cast<int>(i+2);
921 const size_t size = data.size();
933 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
938 if (i+2<size && data[i+1]==c && data[i+2]==c)
942 out+=
"</strong></em>";
944 return static_cast<int>(i+3);
946 else if (i+1<size && data[i+1]==c)
957 return static_cast<int>(len - 2);
971 return static_cast<int>(len - 1);
982 const size_t size = data.size();
986 if (i<size && data[i]==
'-')
991 if (i<size && data[i]==
'-')
996 if (i<size && data[i]==
'-')
1000 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
1002 if (count==2 && size > 2 && data[2]==
'>')
1004 if (count==3 && size > 3 && data[3]==
'>')
1006 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1026 const size_t size = data.size();
1029 while (i<size && data[i]!=
'"' && nl<2)
1031 if (data[i]==
'\n') nl++;
1034 if (i<size && data[i]==
'"' && nl<2)
1036 out+=data.substr(0,i+1);
1038 return static_cast<int>(i+1);
1050 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1052 const size_t size = data.size();
1058 while (i < size &&
isIdChar(data[i]))
1063 QCString tagName(data.substr(1,i-1));
1064 if (tagName.
lower()==
"pre")
1066 bool insideStr=
FALSE;
1070 if (!insideStr && c==
'<')
1072 if (data[i+1]==
'/' &&
1073 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1074 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1076 if (doWrite)
out+=data.substr(0,i+6);
1079 return static_cast<int>(i+6);
1082 else if (insideStr && c==
'"')
1084 if (data[i-1]!=
'\\') insideStr=
FALSE;
1097 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1100 if (doWrite)
out+=data.substr(0,i+2);
1102 return static_cast<int>(i+2);
1104 else if (data[i]==
'>')
1107 if (doWrite)
out+=data.substr(0,i+1);
1109 return static_cast<int>(i+1);
1111 else if (data[i]==
' ')
1114 bool insideAttr=
FALSE;
1117 if (!insideAttr && data[i]==
'"')
1121 else if (data[i]==
'"' && data[i-1]!=
'\\')
1125 else if (!insideAttr && data[i]==
'>')
1128 if (doWrite)
out+=data.substr(0,i+1);
1130 return static_cast<int>(i+1);
1150 const size_t size = data.size();
1162 if (size>2 && c!=
'~' && data[1]!=c)
1165 if (data[1]==
' ' || data[1]==
'\n' ||
1173 if (size>3 && data[1]==c && data[2]!=c)
1175 if (data[2]==
' ' || data[2]==
'\n' ||
1183 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1185 if (data[3]==
' ' || data[3]==
'\n' ||
1197 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1202 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1213 out+=link.
mid(fd ? 0 : 5);
1214 if (!explicitTitle && !content.
isEmpty())
1220 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1242 const size_t size = data.size();
1246 bool isImageLink =
FALSE;
1247 bool isImageInline =
FALSE;
1253 if (size<2 || data[1]!=
'[')
1262 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1264 if (data.data()[pos]==
'\n') numNLsNeeded--;
1265 else if (data.data()[pos]!=
' ')
1275 size_t contentStart=i;
1282 if (data[i-1]==
'\\')
1285 else if (data[i]==
'[')
1289 else if (data[i]==
']')
1292 if (level<=0)
break;
1294 else if (data[i]==
'\n')
1297 if (nl>1) {
return 0; }
1303 if (i>=size)
return 0;
1304 size_t contentEnd=i;
1305 content = data.substr(contentStart,contentEnd-contentStart);
1307 if (!isImageLink && content.
isEmpty()) {
return 0; }
1310 bool whiteSpace =
false;
1312 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1313 if (i<size && data[i]==
'\n')
1318 while (i<size && data[i]==
' ') i++;
1320 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1322 bool explicitTitle=
FALSE;
1323 if (i<size && data[i]==
'(')
1326 while (i<size && data[i]==
' ') i++;
1327 bool uriFormat=
false;
1328 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1332 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1338 if (nlConsec>1) {
return 0; }
1340 else if (data[i]==
'(')
1345 else if (data[i]==
')')
1350 else if (data[i]!=
' ')
1361 if (i>=size || data[i]==
'\n') {
return 0; }
1362 link = data.substr(linkStart,i-linkStart);
1365 if (link.
isEmpty()) {
return 0; }
1369 if (data[i]==
'\'' || data[i]==
'"')
1373 size_t titleStart=i;
1379 if (nl>1) {
return 0; }
1382 else if (data[i]==
'\\')
1386 else if (data[i]==c)
1397 size_t titleEnd = i-1;
1399 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1400 if (data[titleEnd]==c)
1402 title = data.substr(titleStart,titleEnd-titleStart);
1406 if (data[i]==
' ')i++;
1407 else if (data[i] ==
')')
break;
1421 else if (i<size && data[i]==
'[')
1427 while (i<size && data[i]!=
']')
1432 if (nl>1) {
return 0; }
1436 if (i>=size) {
return 0; }
1438 link = data.substr(linkStart,i-linkStart);
1450 link = lr_it->second.link;
1451 title = lr_it->second.title;
1461 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1468 link = lr_it->second.link;
1469 title = lr_it->second.title;
1473 else if (content==
"TOC")
1496 while (j<size && data[j]==
' ') { j++; }
1497 if (j<size && data[j]==
'{')
1502 size_t attributesStart=i;
1507 if (data[i-1]==
'\\')
1510 else if (data[i]==
'{')
1514 else if (data[i]==
'}')
1517 if (level<=0)
break;
1519 else if (data[i]==
'\n')
1522 if (nl>1) {
return 0; }
1527 if (i>=size)
return 0;
1528 size_t attributesEnd=i;
1529 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1538 while (pos<size && numNLsNeeded>0)
1540 if (data[pos]==
'\n') numNLsNeeded--;
1541 else if (data[pos]!=
' ')
1556 out+=
"@tableofcontents{html:";
1561 else if (isImageLink)
1565 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1570 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1571 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1572 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1573 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1574 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1596 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1628 if (explicitTitle && !title.
isEmpty())
1638 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1640 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1653 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1675 return static_cast<int>(i);
1682 const size_t size = data.size();
1686 while (nb<size && data[nb]==
'`')
1703 if (
end+1<size && data[
end+1]==
'`')
1718 if (
end+1<size && data[
end+1]==
'`')
1731 else if (data[
end]==
'\n')
1742 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1745 out+=data.substr(nb,
end-nb);
1748 return static_cast<int>(
end+1);
1750 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1753 out+=data.substr(nb,
end-nb);
1756 return static_cast<int>(
end+2);
1760 if (data[
end]!=
' ') pc = data[
end];
1764 if (i < nb && end >= size)
1769 out+=data.substr(0,nb);
1770 return static_cast<int>(nb);
1774 while (
end<size && data[
end]==
'`')
1784 QCString codeFragment = data.substr(nb,
end-nb-nb);
1790 return static_cast<int>(
end);
1809 const size_t size = data.size();
1815 size_t l = endBlockName.
length();
1818 if ((data[i]==
'\\' || data[i]==
'@') &&
1819 data[i-1]!=
'\\' && data[i-1]!=
'@')
1826 return static_cast<int>(i+1+l);
1835 out+=data.substr(0,endPos);
1836 return static_cast<int>(endPos);
1838 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1841 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1847 else if (c==
'\\' || c==
'@')
1849 out+=data.substr(0,2);
1853 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1855 out+=data.substr(1,3);
1859 else if (c==
'-' && size>2 && data[2]==
'-')
1861 out+=data.substr(1,2);
1875 const size_t size = data.size();
1881 out+=data.substr(i,
end-i);
1882 if (
end>=size)
break;
1885 int iend = action(*
this,data.substr(i),i);
1903 const size_t size = data.size();
1904 while (i<size && data[i]==
' ') i++;
1905 if (i==size)
return 0;
1910 while (i < size && data[i] ==
'=')
1915 while (i<size && data[i]==
' ') i++;
1916 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1917 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1932 while (i < size && data[i] ==
'-')
1937 while (i<size && data[i]==
' ') i++;
1938 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1948 const size_t size = data.size();
1949 while (i<size && data[i]==
' ') i++;
1954 while (i<size && (data[i]==
'>' || data[i]==
' '))
1956 if (data[i]==
'>') level++;
1961 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1976 const size_t size = data.size();
1979 while (i<size && data[i]==
' ') i++;
1980 if (i>=size || data[i]!=
'[') {
return 0; }
1982 size_t refIdStart=i;
1983 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1984 if (i>=size || data[i]!=
']') {
return 0; }
1985 refid = data.substr(refIdStart,i-refIdStart);
1986 if (refid.
isEmpty()) {
return 0; }
1990 if (i>=size || data[i]!=
':') {
return 0; }
1994 while (i<size && data[i]==
' ') i++;
1995 if (i<size && data[i]==
'\n')
1998 while (i<size && data[i]==
' ') i++;
2000 if (i>=size) {
return 0; }
2002 if (i<size && data[i]==
'<') i++;
2004 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2006 if (i<size && data[i]==
'>') i++;
2007 if (linkStart==linkEnd) {
return 0; }
2008 link = data.substr(linkStart,linkEnd-linkStart);
2010 if (link==
"@ref" || link==
"\\ref")
2013 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2014 link+=data.substr(argStart,i-argStart);
2021 while (i<size && data[i]==
' ') i++;
2022 if (i<size && data[i]==
'\n')
2026 while (i<size && data[i]==
' ') i++;
2030 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2035 if (c==
'\'' || c==
'"' || c==
'(')
2040 size_t titleStart=i;
2042 while (i<size && data[i]!=
'\n') i++;
2047 while (
end>titleStart && data[
end]!=c)
end--;
2050 title = data.substr(titleStart,
end-titleStart);
2054 while (i<size && data[i]==
' ') i++;
2066 size_t size = data.size();
2067 if (size>0 && data[size-1]==
'\n') size--;
2068 while (i<size && data[i]==
' ') i++;
2069 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2071 if (c!=
'*' && c!=
'-' && c!=
'_')
2083 else if (data[i]!=
' ')
2098 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2100 std::string ti = title.str();
2103 std::string
id = match[1].str();
2104 title = title.
left(match.position());
2107 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2113 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2116 if (pIsIdGenerated) *pIsIdGenerated=
true;
2131 int level = 0, blanks=0;
2132 const size_t size = data.size();
2135 while (i<size && data[i]==
' ') i++;
2136 if (i>=size || data[i]!=
'#')
2140 while (i < size && data[i] ==
'#')
2149 while (i < size && data[i] ==
' ')
2154 if (level==1 && blanks==0)
2161 while (
end<size && data[
end]!=
'\n')
end++;
2162 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2165 header = data.substr(i,
end-i);
2169 int idx=
static_cast<int>(header.
length())-1;
2170 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2171 header=header.
left(idx+1);
2174 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2205 while (i<data.size())
2216 (data[(i)]=='<' && \
2217 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2218 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2227 const size_t size=data.size();
2231 bool listMarkerSkipped=
FALSE;
2234 (!listMarkerSkipped &&
2235 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2236 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2237 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2238 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2247 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2251 if (j+1<size && data[j+1]==
' ')
2253 listMarkerSkipped=
TRUE;
2270 listMarkerSkipped=
TRUE;
2272 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2274 listMarkerSkipped=
TRUE;
2278 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2280 listMarkerSkipped=
TRUE;
2282 if (data[i]!=
' ' && !listMarkerSkipped)
2296 size_t normalIndent = 0;
2297 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2299 size_t result = listIndent>normalIndent ? listIndent : 0;
2310 while (i<data.size())
2316 else if (data[i]==
'\n')
2320 else if (data[i]!=
' ' && data[i]!=
'\t')
2332 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2336 const char dot =
'.';
2337 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2338 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2339 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2345 const size_t size = data.size();
2346 while (i < size && data[i] ==
' ')
2351 if (indent>=refIndent+4)
2353 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2357 if (i<size && data[i]==
'`') tildaChar=
'`';
2358 while (i < size && data[i] == tildaChar)
2365 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2369 while (i<size && data[i]==
' ') { i++; }
2370 if (i<size && data[i]==
'{')
2373 if (data[i] == dot) i++;
2375 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2376 if (i<size && data[i]==
'}')
2378 lang = data.substr(startLang,i-startLang);
2387 else if (i<size && isLangChar(data[i]))
2389 if (data[i] == dot) i++;
2391 if (i<size && isAlphaChar(data[i]))
2394 while (i<size && isAlphaNChar(data[i])) i++;
2396 lang = data.substr(startLang,i-startLang);
2406 if (data[i]==tildaChar)
2410 while (i < size && data[i] == tildaChar)
2415 while (i<size && data[i]==
' ') i++;
2417 if (endTildes==startTildes)
2420 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2427 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2432static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2439 const size_t size = data.size();
2440 while (i < size && data[i] ==
' ')
2448 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2451 if (indent0>=size || data[indent0]==
'\n')
2453 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2460 int offset_i =
static_cast<int>(offset);
2464 int j =
static_cast<int>(i)-offset_i-1;
2466 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2469 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2475 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2486 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2495 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2501 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2508 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2516 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2532 const size_t size = data.size();
2535 while (i<size && data[i]==
' ') i++;
2536 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2545 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2548 if (j>0 && i>0) i--;
2549 while (i>0 && data[i]==
' ') i--;
2550 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2564 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2565 if (columns==1) columns++;
2569 if (n==2 && columns==0)
2581 size_t cc0=0, start=0,
end=0;
2585 if (i>=data.size() || cc0<1)
2597 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2606 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2621 const size_t size = data.size();
2623 size_t columns=0, start=0,
end=0;
2625 size_t headerStart = start;
2626 size_t headerEnd =
end;
2632 std::vector<Alignment> columnAlignment(columns);
2634 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2640 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2641 if (data[j]==
'-') startFound=
TRUE;
2644 if (data[j]==
'-') rightMarker=
FALSE;
2645 else if (data[j]==
':') rightMarker=
TRUE;
2646 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2670 std::vector<std::vector<TableCell> > tableContents;
2672 size_t m = headerStart;
2673 std::vector<TableCell> headerContents(columns);
2674 for (k=0;k<columns;k++)
2676 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2678 headerContents[k].cellText += data[m++];
2683 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2684 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2686 tableContents.push_back(headerContents);
2692 if (cc!=columns)
break;
2696 std::vector<TableCell> rowContents(columns);
2699 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2703 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2704 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2709 rowContents[k].cellText += data[j];
2715 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2716 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2717 tableContents.push_back(rowContents);
2723 out+=
"<table class=\"markdownTable\">";
2724 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2725 for (
size_t row = 0; row < tableContents.size(); row++)
2731 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2735 out+=
"\n<tr class=\"markdownTableRowEven\">";
2740 out+=
"\n <tr class=\"markdownTableHead\">";
2742 for (
size_t c = 0; c < columns; c++)
2745 QCString cellText(tableContents[row][c].cellText);
2751 if (tableContents[row][c].cellText ==
"^")
2755 if (tableContents[row][c].colSpan)
2757 int cr =
static_cast<int>(c);
2758 while ( cr >= 0 && tableContents[row][cr].colSpan)
2762 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2764 size_t rowSpan = 1, spanRow = row+1;
2765 while ((spanRow < tableContents.size()) &&
2766 (tableContents[spanRow][c].cellText ==
"^"))
2772 out+=
" <" + cellTag +
" " + cellClass;
2774 switch (columnAlignment[c])
2786 out+=
" rowspan=\"" + spanStr +
"\"";
2792 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2801 out+=
" colspan=\"" + spanStr +
"\"";
2805 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2808 cellClass =
"class=\"markdownTableBody";
2824 while (i<data.size() && data[i]!=
'\n')
2826 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2829 if (i>=data.size()) {
return 0; }
2830 if (i<2) {
return 0; }
2831 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2871 out+=
"</"+hTag+
">\n";
2874 else if (data.size()>0)
2876 size_t tmpSize = data.size();
2877 if (data[data.size()-1] ==
'\n') tmpSize--;
2878 out+=data.substr(0,tmpSize);
2882 out+=
"\\ilinebr<br>";
2884 if (tmpSize != data.size())
out+=
'\n';
2890 {
"[!note]",
"\\note" },
2891 {
"[!warning]",
"\\warning" },
2892 {
"[!tip]",
"\\remark" },
2893 {
"[!caution]",
"\\attention" },
2894 {
"[!important]",
"\\important" }
2903 const size_t size = data.size();
2904 std::string startCmd;
2905 int isGitHubAlert =
false;
2906 int isGitHubFirst =
false;
2911 while (
end<=size && data[
end-1]!=
'\n')
end++;
2916 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2918 if (data[j]==
'>') { level++; indent=j+1; }
2919 else if (j>0 && data[j-1]==
'>') indent=j+1;
2922 if (indent>0 && j>0 && data[j-1]==
'>' &&
2923 !(j==size || data[j]==
'\n'))
2940 isGitHubAlert =
true;
2941 isGitHubFirst =
true;
2942 startCmd = it->second;
2947 if (level!=1 || !isGitHubAlert)
2949 for (
int l=curLevel;l<level-1;l++)
2951 out+=
"<blockquote>";
2953 out +=
"<blockquote>‍";
2955 else if (!startCmd.empty())
2957 out += startCmd +
" ";
2960 else if (level<curLevel)
2963 if (level==0 && isGitHubAlert)
2969 out +=
"</blockquote>\\ilinebr ";
2978 if (curLevel!=0 || !isGitHubAlert)
2980 std::string_view txt = data.substr(indent,
end-indent);
2983 if (!isGitHubFirst)
out +=
"<br>";
2990 isGitHubFirst =
false;
3005 for (
int l=0;l<curLevel;l++)
3007 out+=
"</blockquote>";
3018 size_t size = data.size();
3019 while (i<data.size() && data[i]==
' ') i++;
3022 size_t locStart = i;
3023 if (i>offset) locStart--;
3026 while (i+9<size && data[i]!=
'\n')
3038 location=data.substr(locStart,i-locStart);
3040 while (indent > 0 && i < size && data[i] ==
' ')
3045 if (i<size && data[i]==
'\n') i++;
3056 const size_t size = data.size();
3059 out+=
"@iverbatim\n";
3061 std::string location;
3066 while (
end<=size && data[
end-1]!=
'\n')
end++;
3069 while (j <
end && data[j] ==
' ')
3083 while (emptyLines>0)
3091 std::string lineLoc;
3096 out+=data.substr(offset,
end-offset);
3104 out+=
"@endiverbatim";
3105 if (!location.empty())
3113 while (emptyLines>0)
3129 const size_t size = data.size();
3130 size_t nb=0,
end=offset+1, j=0;
3135 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3136 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3143 size_t l = endBlockName.
length();
3144 for (;
end+l+1<size;
end++)
3146 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3147 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3161 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3162 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3165 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3166 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3177 else if (nb==0 && data[
end-1]==
'`')
3179 while (
end <= size && data[
end - 1] ==
'`')
3185 else if (nb>0 && data[
end-1]==
'`')
3188 while (
end <= size && data[
end - 1] ==
'`')
3206 size_t blockStart,
size_t blockEnd)
3209 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3210 const size_t size=data.size();
3212 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3233 size_t pi=std::string::npos;
3234 bool newBlock =
false;
3235 bool insideList =
false;
3236 size_t currentIndent = refIndent;
3237 size_t listIndent = refIndent;
3238 const size_t size = data.size();
3245 size_t lineIndent=0;
3246 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3252 if (insideList && lineIndent<currentIndent)
3255 currentIndent = refIndent;
3263 if (listIndent<currentIndent+4)
3267 currentIndent = listIndent;
3274 currentIndent = listIndent;
3283 if (pi!=std::string::npos)
3285 size_t blockStart=0, blockEnd=0, blockOffset=0;
3288 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3290 size_t cmdPos = pi+blockStart+1;
3291 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3297 if (pl[ii]==
'\n') nl++;
3300 bool addNewLines =
false;
3302 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3311 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3312 addNewLines =
false;
3327 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3329 if (addNewLines)
out+=
'\n';
3334 addSpecialCommand(
"startuml",
"enduml");
3338 addSpecialCommand(
"dot",
"enddot");
3340 else if (lang==
"msc")
3342 addSpecialCommand(
"msc",
"endmsc");
3344 else if (lang==
"mermaid")
3346 addSpecialCommand(
"mermaid",
"endmermaid");
3353 pi=std::string::npos;
3357 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3360 pi=std::string::npos;
3367 out+=data.substr(pi,i-pi);
3373 if (pi!=std::string::npos && pi<size)
3383 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3385 out+=data.substr(pi);
3399 size_t pi = std::string::npos;
3415 size_t currentIndent = indent;
3416 size_t listIndent = indent;
3417 bool insideList =
false;
3418 bool newBlock =
false;
3421 while (i<data.size())
3426 size_t lineIndent=0;
3428 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3434 if (insideList && lineIndent<currentIndent)
3437 currentIndent = indent;
3445 if (listIndent<currentIndent+4)
3449 currentIndent = listIndent;
3456 currentIndent = listIndent;
3468 if (pi!=std::string::npos)
3470 size_t blockStart=0, blockEnd=0, blockOffset=0;
3472 size_t blockIndent = currentIndent;
3476 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3480 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3490 size_t l = endBlockName.
length();
3491 while (i+l<data.size())
3493 if ((data[i]==
'\\' || data[i]==
'@') &&
3494 data[i-1]!=
'\\' && data[i-1]!=
'@')
3511 while (pi<data.size() && data[pi]==
' ') pi++;
3512 QCString header = data.substr(pi,i-pi-1);
3519 out+=level==1?
"@section ":
"@subsection ";
3527 out+=level==1?
"<h1>":
"<h2>";
3529 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3536 pi=std::string::npos;
3541 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3555 pi=std::string::npos;
3563 pi=std::string::npos;
3570 pi=std::string::npos;
3583 if (pi!=std::string::npos && pi<data.size())
3585 if (
isLinkRef(data.substr(pi),
id,link,title))
3602#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3603 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3606 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3607 OPC(idlexcept);
OPC(file);
3617 std::string_view data(docs.
str());
3618 const size_t size = data.size();
3621 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3628 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3634 (data[i]==
'\\' || data[i]==
'@') &&
3649 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3667 std::string_view data(docs_org.
str());
3668 const size_t size = data.size();
3670 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3672 if (data[i]==
'\n') prepend++;
3675 if (i>=size) {
return QCString(); }
3677 while (end1<size && data[end1-1]!=
'\n') end1++;
3684 while (end2<size && data[end2-1]!=
'\n') end2++;
3685 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3687 title = data.substr(i,end1-i-1);
3688 docs+=
"\n\n"+docs_org.
mid(end2);
3689 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3695 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3698 docs+=docs_org.
mid(end1);
3703 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3714 if (input.
isEmpty())
return input;
3719 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3720 s =
detab(s,refIndent);
3724 s =
prv->processQuotations(s.
view(),refIndent);
3728 s =
prv->processBlocks(s.
view(),refIndent);
3746 const char *p = result.
data();
3749 while (*p==
' ') p++;
3750 while (*p==
'\n') {startNewlines++;p++;};
3753 if (p>result.
data())
3756 result = result.
mid(
static_cast<int>(p-result.
data()));
3769 if (i!=-1) baseFn = baseFn.
left(i);
3793 const char *fileBuf,
3794 const std::shared_ptr<Entry> &root,
3797 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3799 current->lang = SrcLangExt::Markdown;
3800 current->fileName = fileName;
3801 current->docFile = fileName;
3802 current->docLine = 1;
3808 bool isIdGenerated =
false;
3816 int indentLevel=title.
isEmpty() ? 0 : -1;
3823 bool wasEmpty =
id.isEmpty();
3824 if (wasEmpty)
id = mdFileNameId;
3830 if (!mdfileAsMainPage.
isEmpty() &&
3834 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3835 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3837 else if (
id==
"mainpage" ||
id==
"index")
3839 if (title.
isEmpty()) title = titleFn;
3840 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3841 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3843 else if (isSubdirDocs)
3847 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3849 docs.
prepend(
"@dir\\ilinebr ");
3860 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3862 else if (!generatedId.
isEmpty())
3864 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3866 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3869 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3871 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3873 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3878 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3880 std::string s = docs.str();
3883 QCString orgLabel = match[1].str();
3884 QCString orgTitle = match[2].str();
3887 docs = docs.
left(match[1].position())+
3890 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3902 p->commentScanner.enterFile(fileName,lineNr);
3904 bool needsEntry =
false;
3908 while (
p->commentScanner.parseCommentBlock(
3926 QCString docFile = current->docFile;
3927 root->moveToSubEntryAndRefresh(current);
3928 current->lang = SrcLangExt::Markdown;
3929 current->docFile = docFile;
3930 current->docLine = lineNr;
3935 root->moveToSubEntryAndKeep(current);
3937 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.