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
78 ((c>='a' && c<='z') || \
79 (c>='A' && c<='Z') || \
80 (c>='0' && c<='9') || \
81 (static_cast<unsigned char>(c)>=0x80))
85 (c=='-' || c=='+' || c=='!' || \
86 c=='?' || c=='$' || c=='@' || \
87 c=='&' || c=='*' || c=='_' || c=='%' || \
88 c=='[' || c=='(' || c=='.' || \
89 c=='>' || c==':' || c==',' || \
90 c==';' || c=='\'' || c=='"' || c=='`')
92// is character at position i in data allowed before an emphasis section
93#define isOpenEmphChar(c) \
94 (c=='\n' || c==' ' || c=='\'' || c=='<' || \
95 c=='>' || c=='{' || c=='(' || c=='[' || \
96 c==',' || c==':' || c==';')
98// is character at position i in data an escape that prevents ending an emphasis section
99// so for example *bla (*.txt) is cool*
100#define ignoreCloseEmphChar(c,cn) \
101 (c=='(' || c=='{' || c=='[' || (c=='<' && cn!='/') || \
108 TableCell() : colSpan(false) {}
113struct Markdown::Private
115 Private(const QCString &fn,int line,int indent)
116 : fileName(fn), lineNr(line), indentLevel(indent)
118 // setup callback table for special characters
119 actions[static_cast<unsigned int>('_')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
120 actions[static_cast<unsigned int>('*')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
121 actions[static_cast<unsigned int>('~')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
122 actions[static_cast<unsigned int>('`')] = [this](std::string_view data,size_t offset) { return processCodeSpan (data,offset); };
123 actions[static_cast<unsigned int>('\\')]= [this](std::string_view data,size_t offset) { return processSpecialCommand(data,offset); };
124 actions[static_cast<unsigned int>('@')] = [this](std::string_view data,size_t offset) { return processSpecialCommand(data,offset); };
125 actions[static_cast<unsigned int>('[')] = [this](std::string_view data,size_t offset) { return processLink (data,offset); };
126 actions[static_cast<unsigned int>('!')] = [this](std::string_view data,size_t offset) { return processLink (data,offset); };
127 actions[static_cast<unsigned int>('<')] = [this](std::string_view data,size_t offset) { return processHtmlTag (data,offset); };
128 actions[static_cast<unsigned int>('-')] = [this](std::string_view data,size_t offset) { return processNmdash (data,offset); };
129 actions[static_cast<unsigned int>('"')] = [this](std::string_view data,size_t offset) { return processQuoted (data,offset); };
132 QCString processQuotations(std::string_view data,
size_t refIndent);
133 QCString processBlocks(std::string_view data,
size_t indent);
134 QCString isBlockCommand(std::string_view data,
size_t offset);
135 size_t isSpecialCommand(std::string_view data,
size_t offset);
136 size_t findEndOfLine(std::string_view data,
size_t offset);
137 int processHtmlTagWrite(std::string_view data,
size_t offset,
bool doWrite);
138 int processHtmlTag(std::string_view data,
size_t offset);
139 int processEmphasis(std::string_view data,
size_t offset);
140 int processEmphasis1(std::string_view data,
char c);
141 int processEmphasis2(std::string_view data,
char c);
142 int processEmphasis3(std::string_view data,
char c);
143 int processNmdash(std::string_view data,
size_t offset);
144 int processQuoted(std::string_view data,
size_t offset);
145 int processCodeSpan(std::string_view data,
size_t offset);
146 int processSpecialCommand(std::string_view data,
size_t offset);
147 int processLink(std::string_view data,
size_t offset);
148 size_t findEmphasisChar(std::string_view,
char c,
size_t c_size);
149 void addStrEscapeUtf8Nbsp(std::string_view data);
150 void processInline(std::string_view data);
151 void writeMarkdownImage(std::string_view
fmt,
bool inline_img,
bool explicitTitle,
155 int isHeaderline(std::string_view data,
bool allowAdjustLevel);
156 int isAtxHeader(std::string_view data,
QCString &header,
QCString &
id,
bool allowAdjustLevel,
157 bool *pIsIdGenerated=
nullptr);
158 void writeOneLineHeaderOrRuler(std::string_view data);
159 void writeFencedCodeBlock(std::string_view data, std::string_view lang,
160 size_t blockStart,
size_t blockEnd);
161 size_t writeBlockQuote(std::string_view data);
162 size_t writeCodeBlock(std::string_view,
size_t refIndent);
163 size_t writeTableBlock(std::string_view data);
164 QCString extractTitleId(
QCString &title,
int level,
bool *pIsIdGenerated=
nullptr);
172 using Action_t = std::function<int(std::string_view,
size_t)>;
183 :
prv(std::make_unique<
Private>(fileName,lineNr,indentLevel))
185 using namespace std::placeholders;
210 if (data[0] ==
'\n')
return 1;
214 return (data.size()>8 && data[8]==
' ') ? 9 : 8;
225 const char *p=s.
data();
229 if (c==
'"' && pc!=
'\\') result+=
'\\';
242 bool insideQuote=
FALSE;
244 const char *p=s.
data();
251 if (pc!=
'\\') { insideQuote=!insideQuote; }
261 if ((p[0]==
':') && (p[1]==
':'))
273 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
274 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
277 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
278 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
279 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
294 if (leftMarker && rightMarker)
302 else if (rightMarker)
317 for (
const auto &attr_ : attrList)
320 int i = attr.
find(
':');
327 return attr.
mid(i+1);
364 using EndBlockFunc =
QCString (*)(
const std::string &,bool,char);
366 static const auto getEndBlock = [](
const std::string &blockName,bool,char) ->
QCString
368 return "end"+blockName;
370 static const auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
QCString
372 return openBracket ?
QCString(
"}") :
"end"+blockName;
374 static const auto getEndUml = [](
const std::string &,bool,char) ->
QCString
378 static const auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
QCString
382 case '$':
return "f$";
383 case '(':
return "f)";
384 case '[':
return "f]";
385 case '{':
return "f}";
391 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
393 {
"dot", getEndBlock },
394 {
"code", getEndCode },
395 {
"icode", getEndBlock },
396 {
"msc", getEndBlock },
397 {
"verbatim", getEndBlock },
398 {
"iverbatim", getEndBlock },
399 {
"iliteral", getEndBlock },
400 {
"latexonly", getEndBlock },
401 {
"htmlonly", getEndBlock },
402 {
"xmlonly", getEndBlock },
403 {
"rtfonly", getEndBlock },
404 {
"manonly", getEndBlock },
405 {
"docbookonly", getEndBlock },
406 {
"startuml", getEndUml },
407 {
"f", getEndFormula }
410 const size_t size = data.
size();
411 bool openBracket = offset>0 && data.data()[-1]==
'{';
412 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
416 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
418 std::string blockName(data.substr(1,
end-1));
419 auto it = blockNames.find(blockName);
421 if (it!=blockNames.end())
423 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
433 using EndCmdFunc = size_t (*)(std::string_view,size_t);
435 static const auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
440 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
442 if (c==
'\\') lc=
'\\';
443 else if (c!=
' ') lc=0;
449 static const auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
451 if (offset_<data_.size() && data_[offset_]==
' ')
459 while (offset_<data_.size() && data_[offset_]==
' ')
464 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
469 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
471 size_t off = offset_;
472 while (off<data_.size() && data_[off]==
' ')
476 if (off<data_.size() && data_[off]==
',')
495 static const auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
497 return endOfLabels(data_,offset_,
false);
500 static const auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
502 size_t index=offset_;
503 if (index<data_.size() && data_[index]==
' ')
506 while (index<data_.size() && data_[index]==
' ') index++;
508 if (index<data_.size() && data_[index]==
'{')
512 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
513 if (index==data_.size() || data_[index]!=
'}')
return 0;
516 return endOfLabel(data_,offset_);
519 static const auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
521 size_t index=offset_;
522 if (index<data_.size() && data_[index]==
' ')
525 while (index<data_.size() && data_[index]==
' ') index++;
527 if (index<data_.size() && data_[index]==
'[')
531 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
532 if (index==data_.size() || data_[index]!=
']')
return 0;
535 return endOfLabels(data_,offset_,
true);
538 static const auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
540 return endOfLabels(data_,offset_,
true);
543 static const auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
545 if (offset_<data_.size() && data_[offset_]==
' ')
550 while (offset_<data_.size() && data_[offset_]==
' ')
555 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
557 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
564 while (offset_<data_.size() && (c=data_[offset_++]))
567 else if (c==
')') count--;
568 if (count==0)
return offset_;
576 static const auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
578 return endOfFuncLike(data_,offset_,
true);
581 static const auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
583 return endOfFuncLike(data_,offset_,
false);
586 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
589 {
"addindex", endOfLine },
590 {
"addtogroup", endOfLabel },
591 {
"anchor", endOfLabel },
594 {
"category", endOfLine },
595 {
"cite", endOfLabel },
596 {
"class", endOfLine },
597 {
"concept", endOfLine },
598 {
"copybrief", endOfFunc },
599 {
"copydetails", endOfFunc },
600 {
"copydoc", endOfFunc },
601 {
"def", endOfFunc },
602 {
"defgroup", endOfLabel },
603 {
"diafile", endOfLine },
604 {
"dir", endOfLine },
605 {
"dockbookinclude",endOfLine },
606 {
"dontinclude", endOfLine },
607 {
"dotfile", endOfLine },
609 {
"elseif", endOfGuard },
610 {
"em", endOfLabel },
611 {
"emoji", endOfLabel },
612 {
"enum", endOfLabel },
613 {
"example", endOfLine },
614 {
"exception", endOfLine },
615 {
"extends", endOfLabel },
616 {
"file", endOfLine },
618 {
"headerfile", endOfLine },
619 {
"htmlinclude", endOfLine },
620 {
"ianchor", endOfLabelOpt },
621 {
"idlexcept", endOfLine },
622 {
"if", endOfGuard },
623 {
"ifnot", endOfGuard },
624 {
"image", endOfLine },
625 {
"implements", endOfLine },
626 {
"include", endOfLine },
627 {
"includedoc", endOfLine },
628 {
"includelineno", endOfLine },
629 {
"ingroup", endOfLabel },
630 {
"interface", endOfLine },
631 {
"latexinclude", endOfLine },
632 {
"maninclude", endOfLine },
633 {
"memberof", endOfLabel },
634 {
"mscfile", endOfLine },
635 {
"namespace", endOfLabel },
636 {
"noop", endOfLine },
637 {
"overload", endOfLine },
639 {
"package", endOfLabel },
640 {
"page", endOfLabel },
641 {
"paragraph", endOfLabel },
642 {
"param", endOfParam },
643 {
"property", endOfLine },
644 {
"protocol", endOfLine },
645 {
"qualifier", endOfLine },
646 {
"ref", endOfLabel },
647 {
"refitem", endOfLine },
648 {
"related", endOfLabel },
649 {
"relatedalso", endOfLabel },
650 {
"relates", endOfLabel },
651 {
"relatesalso", endOfLabel },
652 {
"retval", endOfRetVal},
653 {
"rtfinclude", endOfLine },
654 {
"section", endOfLabel },
655 {
"skip", endOfLine },
656 {
"skipline", endOfLine },
657 {
"snippet", endOfLine },
658 {
"snippetdoc", endOfLine },
659 {
"snippetlineno", endOfLine },
660 {
"struct", endOfLine },
661 {
"subpage", endOfLabel },
662 {
"subparagraph", endOfLabel },
663 {
"subsubparagraph",endOfLabel },
664 {
"subsection", endOfLabel },
665 {
"subsubsection", endOfLabel },
666 {
"throw", endOfLabel },
667 {
"throws", endOfLabel },
668 {
"tparam", endOfLabel },
669 {
"typedef", endOfLine },
670 {
"plantumlfile", endOfLine },
671 {
"union", endOfLine },
672 {
"until", endOfLine },
673 {
"var", endOfLine },
674 {
"verbinclude", endOfLine },
675 {
"weakgroup", endOfLabel },
676 {
"xmlinclude", endOfLine },
677 {
"xrefitem", endOfLabel }
680 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
681 if (isEscaped)
return 0;
683 const size_t size = data.size();
685 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
686 if (
end==1)
return 0;
687 std::string cmdName(data.substr(1,
end-1));
689 auto it = cmdNames.find(cmdName);
690 if (it!=cmdNames.end())
693 result = it->second(data,
end);
706 const size_t size = data.size();
710 while (i<size && data[i]!=c &&
711 data[i]!=
'\\' && data[i]!=
'@' &&
712 !(data[i]==
'/' && data[i-1]==
'<') &&
732 while (i+len<size && data[i+len]==c)
739 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
745 return static_cast<int>(i);
753 while (i<size && data[i]==
'`') snb++,i++;
757 while (i<size && enb<snb)
759 if (data[i]==
'`') enb++;
760 if (snb==1 && data[i]==
'\'')
break;
764 else if (data[i]==
'@' || data[i]==
'\\')
770 size_t l = endBlockName.
length();
773 if ((data[i]==
'\\' || data[i]==
'@') &&
774 data[i-1]!=
'\\' && data[i-1]!=
'@')
784 else if (i+1<size &&
isIdChar(data[i+1]))
793 else if (data[i-1]==
'<' && data[i]==
'/')
797 else if (data[i]==
'\n')
800 while (i<size && data[i]==
' ') i++;
801 if (i>=size || data[i]==
'\n')
819 const size_t size = data.size();
822 if (size>1 && data[0]==c && data[1]==c) { i=1; }
827 if (len==0) {
return 0; }
829 if (i>=size) {
return 0; }
831 if (i+1<size && data[i+1]==c)
836 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
842 return static_cast<int>(i+1);
853 const size_t size = data.size();
863 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
865 if (c ==
'~')
out+=
"<strike>";
866 else out+=
"<strong>";
868 if (c ==
'~')
out+=
"</strike>";
869 else out+=
"</strong>";
871 return static_cast<int>(i+2);
885 const size_t size = data.size();
897 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
902 if (i+2<size && data[i+1]==c && data[i+2]==c)
906 out+=
"</strong></em>";
908 return static_cast<int>(i+3);
910 else if (i+1<size && data[i+1]==c)
921 return static_cast<int>(len - 2);
935 return static_cast<int>(len - 1);
946 const size_t size = data.size();
950 if (i<size && data[i]==
'-')
954 if (i<size && data[i]==
'-')
958 if (i<size && data[i]==
'-')
962 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
964 if (count==2 && size > 2 && data[2]==
'>')
966 if (count==3 && size > 3 && data[3]==
'>')
968 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
988 const size_t size = data.size();
991 while (i<size && data[i]!=
'"' && nl<2)
993 if (data[i]==
'\n') nl++;
996 if (i<size && data[i]==
'"' && nl<2)
998 out+=data.substr(0,i+1);
1000 return static_cast<int>(i+1);
1012 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1014 const size_t size = data.size();
1020 while (i<size &&
isIdChar(data[i])) i++,l++;
1021 QCString tagName(data.substr(1,i-1));
1022 if (tagName.
lower()==
"pre")
1024 bool insideStr=
FALSE;
1028 if (!insideStr && c==
'<')
1030 if (data[i+1]==
'/' &&
1031 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1032 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1034 if (doWrite)
out+=data.substr(0,i+6);
1037 return static_cast<int>(i+6);
1040 else if (insideStr && c==
'"')
1042 if (data[i-1]!=
'\\') insideStr=
FALSE;
1055 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1058 if (doWrite)
out+=data.substr(0,i+2);
1060 return static_cast<int>(i+2);
1062 else if (data[i]==
'>')
1065 if (doWrite)
out+=data.substr(0,i+1);
1067 return static_cast<int>(i+1);
1069 else if (data[i]==
' ')
1072 bool insideAttr=
FALSE;
1075 if (!insideAttr && data[i]==
'"')
1079 else if (data[i]==
'"' && data[i-1]!=
'\\')
1083 else if (!insideAttr && data[i]==
'>')
1086 if (doWrite)
out+=data.substr(0,i+1);
1088 return static_cast<int>(i+1);
1108 const size_t size = data.size();
1120 if (size>2 && c!=
'~' && data[1]!=c)
1123 if (data[1]==
' ' || data[1]==
'\n' ||
1131 if (size>3 && data[1]==c && data[2]!=c)
1133 if (data[2]==
' ' || data[2]==
'\n' ||
1141 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1143 if (data[3]==
' ' || data[3]==
'\n' ||
1155 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1160 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1171 out+=link.
mid(fd ? 0 : 5);
1172 if (!explicitTitle && !content.
isEmpty())
1178 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1200 const size_t size = data.size();
1204 bool isImageLink =
FALSE;
1205 bool isImageInline =
FALSE;
1211 if (size<2 || data[1]!=
'[')
1220 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1222 if (data.data()[pos]==
'\n') numNLsNeeded--;
1223 else if (data.data()[pos]!=
' ')
1233 size_t contentStart=i;
1240 if (data[i-1]==
'\\')
1243 else if (data[i]==
'[')
1247 else if (data[i]==
']')
1250 if (level<=0)
break;
1252 else if (data[i]==
'\n')
1255 if (nl>1) {
return 0; }
1261 if (i>=size)
return 0;
1262 size_t contentEnd=i;
1263 content = data.substr(contentStart,contentEnd-contentStart);
1265 if (!isImageLink && content.
isEmpty()) {
return 0; }
1268 bool whiteSpace =
false;
1270 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1271 if (i<size && data[i]==
'\n')
1276 while (i<size && data[i]==
' ') i++;
1278 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1280 bool explicitTitle=
FALSE;
1281 if (i<size && data[i]==
'(')
1284 while (i<size && data[i]==
' ') i++;
1285 bool uriFormat=
false;
1286 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1289 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1294 if (nl>1) {
return 0; }
1296 else if (data[i]==
'(')
1300 else if (data[i]==
')')
1311 if (i>=size || data[i]==
'\n') {
return 0; }
1312 link = data.substr(linkStart,i-linkStart);
1315 if (link.
isEmpty()) {
return 0; }
1319 if (data[i]==
'\'' || data[i]==
'"')
1323 size_t titleStart=i;
1329 if (nl>1) {
return 0; }
1332 else if (data[i]==
'\\')
1336 else if (data[i]==c)
1347 size_t titleEnd = i-1;
1349 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1350 if (data[titleEnd]==c)
1352 title = data.substr(titleStart,titleEnd-titleStart);
1356 if (data[i]==
' ')i++;
1357 else if (data[i] ==
')')
break;
1371 else if (i<size && data[i]==
'[')
1377 while (i<size && data[i]!=
']')
1382 if (nl>1) {
return 0; }
1386 if (i>=size) {
return 0; }
1388 link = data.substr(linkStart,i-linkStart);
1400 link = lr_it->second.link;
1401 title = lr_it->second.title;
1411 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1418 link = lr_it->second.link;
1419 title = lr_it->second.title;
1423 else if (content==
"TOC")
1446 while (j<size && data[j]==
' ') { j++; }
1447 if (j<size && data[j]==
'{')
1452 size_t attributesStart=i;
1457 if (data[i-1]==
'\\')
1460 else if (data[i]==
'{')
1464 else if (data[i]==
'}')
1467 if (level<=0)
break;
1469 else if (data[i]==
'\n')
1472 if (nl>1) {
return 0; }
1477 if (i>=size)
return 0;
1478 size_t attributesEnd=i;
1479 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1488 while (pos<size && numNLsNeeded>0)
1490 if (data[pos]==
'\n') numNLsNeeded--;
1491 else if (data[pos]!=
' ')
1506 out+=
"@tableofcontents{html:";
1511 else if (isImageLink)
1515 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1520 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1521 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1522 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1523 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1524 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1546 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1578 if (explicitTitle && !title.
isEmpty())
1588 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1590 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1603 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1625 return static_cast<int>(i);
1632 const size_t size = data.size();
1636 while (nb<size && data[nb]==
'`')
1650 else if (data[
end]==
'\n')
1653 if (pc ==
'\n')
return 0;
1660 out+=data.substr(nb,
end-nb);
1662 return static_cast<int>(
end+1);
1666 if (data[
end]!=
' ') pc = data[
end];
1670 if (i < nb && end >= size)
1680 QCString codeFragment = data.substr(nb,
end-nb-nb);
1686 return static_cast<int>(
end);
1705 const size_t size = data.size();
1711 size_t l = endBlockName.
length();
1714 if ((data[i]==
'\\' || data[i]==
'@') &&
1715 data[i-1]!=
'\\' && data[i-1]!=
'@')
1722 return static_cast<int>(i+1+l);
1731 out+=data.substr(0,endPos);
1732 return static_cast<int>(endPos);
1734 if (size>1 && data[0]==
'\\')
1737 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1743 else if (c==
'\\' || c==
'@')
1745 out+=data.substr(0,2);
1749 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1751 out+=data.substr(1,3);
1755 else if (c==
'-' && size>2 && data[2]==
'-')
1757 out+=data.substr(1,2);
1762 else if (size>1 && data[0]==
'@')
1765 if (c==
'\\' || c==
'@')
1767 out+=data.substr(0,2);
1781 const size_t size = data.size();
1785 while (
end<size && ((action=
actions[
static_cast<uint8_t
>(data[
end])])==
nullptr))
end++;
1787 out+=data.substr(i,
end-i);
1788 if (
end>=size)
break;
1791 int iend = action(data.substr(i),i);
1809 const size_t size = data.size();
1810 while (i<size && data[i]==
' ') i++;
1811 if (i==size)
return 0;
1816 while (i<size && data[i]==
'=') i++,c++;
1817 while (i<size && data[i]==
' ') i++;
1818 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1819 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1834 while (i<size && data[i]==
'-') i++,c++;
1835 while (i<size && data[i]==
' ') i++;
1836 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1846 const size_t size = data.size();
1847 while (i<size && data[i]==
' ') i++;
1852 while (i<size && (data[i]==
'>' || data[i]==
' '))
1854 if (data[i]==
'>') level++;
1859 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1874 const size_t size = data.size();
1877 while (i<size && data[i]==
' ') i++;
1878 if (i>=size || data[i]!=
'[') {
return 0; }
1880 size_t refIdStart=i;
1881 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1882 if (i>=size || data[i]!=
']') {
return 0; }
1883 refid = data.substr(refIdStart,i-refIdStart);
1884 if (refid.
isEmpty()) {
return 0; }
1888 if (i>=size || data[i]!=
':') {
return 0; }
1892 while (i<size && data[i]==
' ') i++;
1893 if (i<size && data[i]==
'\n')
1896 while (i<size && data[i]==
' ') i++;
1898 if (i>=size) {
return 0; }
1900 if (i<size && data[i]==
'<') i++;
1902 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
1904 if (i<size && data[i]==
'>') i++;
1905 if (linkStart==linkEnd) {
return 0; }
1906 link = data.substr(linkStart,linkEnd-linkStart);
1908 if (link==
"@ref" || link==
"\\ref")
1911 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
1912 link+=data.substr(argStart,i-argStart);
1919 while (i<size && data[i]==
' ') i++;
1920 if (i<size && data[i]==
'\n')
1924 while (i<size && data[i]==
' ') i++;
1928 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
1933 if (c==
'\'' || c==
'"' || c==
'(')
1938 size_t titleStart=i;
1940 while (i<size && data[i]!=
'\n') i++;
1945 while (
end>titleStart && data[
end]!=c)
end--;
1948 title = data.substr(titleStart,
end-titleStart);
1952 while (i<size && data[i]==
' ') i++;
1964 size_t size = data.size();
1965 if (size>0 && data[size-1]==
'\n') size--;
1966 while (i<size && data[i]==
' ') i++;
1967 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
1969 if (c!=
'*' && c!=
'-' && c!=
'_')
1981 else if (data[i]!=
' ')
1996 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
1998 std::string ti = title.str();
2001 std::string
id = match[1].str();
2002 title = title.
left(match.position());
2005 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2011 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2014 if (pIsIdGenerated) *pIsIdGenerated=
true;
2029 int level = 0, blanks=0;
2030 const size_t size = data.size();
2033 while (i<size && data[i]==
' ') i++;
2034 if (i>=size || data[i]!=
'#')
2038 while (i<size && data[i]==
'#') i++,level++;
2043 while (i<size && data[i]==
' ') i++,blanks++;
2044 if (level==1 && blanks==0)
2051 while (
end<size && data[
end]!=
'\n')
end++;
2052 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2055 header = data.substr(i,
end-i);
2059 int idx=
static_cast<int>(header.
length())-1;
2060 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2061 header=header.
left(idx+1);
2064 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2095 while (i<data.size())
2106 (data[(i)]=='<' && \
2107 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2108 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2117 const size_t size=data.size();
2121 bool listMarkerSkipped=
FALSE;
2124 (!listMarkerSkipped &&
2125 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2126 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2127 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2128 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2137 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2141 if (j+1<size && data[j+1]==
' ')
2143 listMarkerSkipped=
TRUE;
2160 listMarkerSkipped=
TRUE;
2162 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2164 listMarkerSkipped=
TRUE;
2168 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2170 listMarkerSkipped=
TRUE;
2172 if (data[i]!=
' ' && !listMarkerSkipped)
2185 size_t normalIndent = 0;
2186 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2188 size_t result = listIndent>normalIndent ? listIndent : 0;
2199 while (i<data.size())
2205 else if (data[i]==
'\n')
2209 else if (data[i]!=
' ' && data[i]!=
'\t')
2221 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset)
2224 const char dot =
'.';
2225 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2226 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2227 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2233 const size_t size = data.size();
2234 while (i<size && data[i]==
' ') indent++,i++;
2235 if (indent>=refIndent+4)
2237 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2241 if (i<size && data[i]==
'`') tildaChar=
'`';
2242 while (i<size && data[i]==tildaChar) startTildes++,i++;
2245 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2248 if (i<size && data[i]==
'{')
2251 if (data[i] == dot) i++;
2253 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2254 if (i<size && data[i]==
'}')
2256 lang = data.substr(startLang,i-startLang);
2265 else if (i<size && isLangChar(data[i]))
2267 if (data[i] == dot) i++;
2269 if (i<size && isAlphaChar(data[i]))
2272 while (i<size && isAlphaNChar(data[i])) i++;
2274 lang = data.substr(startLang,i-startLang);
2284 if (data[i]==tildaChar)
2288 while (i<size && data[i]==tildaChar) endTildes++,i++;
2289 while (i<size && data[i]==
' ') i++;
2291 if (endTildes==startTildes)
2294 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2305static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2312 const size_t size = data.size();
2313 while (i<size && data[i]==
' ') indent0++,i++;
2317 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2320 if (indent0>=size || data[indent0]==
'\n')
2322 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2329 int offset_i =
static_cast<int>(offset);
2333 int j =
static_cast<int>(i)-offset_i-1;
2335 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2338 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2344 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2355 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2364 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2370 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2377 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2385 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2401 const size_t size = data.size();
2404 while (i<size && data[i]==
' ') i++;
2405 if (i<size && data[i]==
'|' && data[i]!=
'\n') i++,n++;
2410 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2413 if (j>0 && i>0) i--;
2414 while (i>0 && data[i]==
' ') i--;
2415 if (i>0 && data[i-1]!=
'\\' && data[i]==
'|') i--,n++;
2425 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2426 if (columns==1) columns++;
2430 if (n==2 && columns==0)
2442 size_t cc0=0, start=0,
end=0;
2446 if (i>=data.size() || cc0<1)
2458 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2467 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2482 const size_t size = data.size();
2484 size_t columns=0, start=0,
end=0;
2486 size_t headerStart = start;
2487 size_t headerEnd =
end;
2493 std::vector<int> columnAlignment(columns);
2495 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2501 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2502 if (data[j]==
'-') startFound=
TRUE;
2505 if (data[j]==
'-') rightMarker=
FALSE;
2506 else if (data[j]==
':') rightMarker=
TRUE;
2507 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2531 std::vector<std::vector<TableCell> > tableContents;
2533 size_t m = headerStart;
2534 std::vector<TableCell> headerContents(columns);
2535 for (k=0;k<columns;k++)
2537 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2539 headerContents[k].cellText += data[m++];
2544 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2545 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2547 tableContents.push_back(headerContents);
2553 if (cc!=columns)
break;
2557 std::vector<TableCell> rowContents(columns);
2560 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2564 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2565 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2570 rowContents[k].cellText += data[j];
2576 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2577 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2578 tableContents.push_back(rowContents);
2584 out+=
"<table class=\"markdownTable\">";
2585 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2586 for (
size_t row = 0; row < tableContents.size(); row++)
2592 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2596 out+=
"\n<tr class=\"markdownTableRowEven\">";
2601 out+=
"\n <tr class=\"markdownTableHead\">";
2603 for (
size_t c = 0; c < columns; c++)
2606 QCString cellText(tableContents[row][c].cellText);
2612 if (tableContents[row][c].cellText ==
"^")
2616 if (tableContents[row][c].colSpan)
2618 int cr =
static_cast<int>(c);
2619 while ( cr >= 0 && tableContents[row][cr].colSpan)
2623 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2625 size_t rowSpan = 1, spanRow = row+1;
2626 while ((spanRow < tableContents.size()) &&
2627 (tableContents[spanRow][c].cellText ==
"^"))
2633 out+=
" <" + cellTag +
" " + cellClass;
2635 switch (columnAlignment[c])
2647 out+=
" rowspan=\"" + spanStr +
"\"";
2653 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2662 out+=
" colspan=\"" + spanStr +
"\"";
2666 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2669 cellClass =
"class=\"markdownTableBody";
2685 while (i<data.size() && data[i]!=
'\n')
2687 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2690 if (i>=data.size()) {
return 0; }
2691 if (i<2) {
return 0; }
2692 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2732 out+=
"</"+hTag+
">\n";
2735 else if (data.size()>0)
2737 size_t tmpSize = data.size();
2738 if (data[data.size()-1] ==
'\n') tmpSize--;
2739 out+=data.substr(0,tmpSize);
2743 out+=
"\\ilinebr<br>";
2745 if (tmpSize != data.size())
out+=
'\n';
2751 {
"[!note]",
"\\note" },
2752 {
"[!warning]",
"\\warning" },
2753 {
"[!tip]",
"\\remark" },
2754 {
"[!caution]",
"\\attention" },
2755 {
"[!important]",
"\\important" }
2764 const size_t size = data.size();
2765 std::string startCmd;
2766 int isGitHubAlert =
false;
2767 int isGitHubFirst =
false;
2772 while (
end<=size && data[
end-1]!=
'\n')
end++;
2777 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2779 if (data[j]==
'>') { level++; indent=j+1; }
2780 else if (j>0 && data[j-1]==
'>') indent=j+1;
2783 if (indent>0 && j>0 && data[j-1]==
'>' &&
2784 !(j==size || data[j]==
'\n'))
2801 isGitHubAlert =
true;
2802 isGitHubFirst =
true;
2803 startCmd = it->second;
2808 if (level!=1 || !isGitHubAlert)
2810 for (
int l=curLevel;l<level-1;l++)
2812 out+=
"<blockquote>";
2814 out +=
"<blockquote>‍";
2816 else if (!startCmd.empty())
2818 out += startCmd +
" ";
2821 else if (level<curLevel)
2824 if (level==0 && isGitHubAlert)
2830 out +=
"</blockquote>\\ilinebr ";
2839 if (curLevel!=0 || !isGitHubAlert)
2841 std::string_view txt = data.substr(indent,
end-indent);
2844 if (!isGitHubFirst)
out +=
"<br>";
2851 isGitHubFirst =
false;
2866 for (
int l=0;l<curLevel;l++)
2868 out+=
"</blockquote>";
2879 size_t size = data.size();
2880 while (i<data.size() && data[i]==
' ') i++;
2883 size_t locStart = i;
2884 if (i>offset) locStart--;
2887 while (i+9<size && data[i]!=
'\n')
2899 location=data.substr(locStart,i-locStart);
2901 while (indent>0 && i<size && data[i]==
' ') i++,indent--;
2902 if (i<size && data[i]==
'\n') i++;
2913 const size_t size = data.size();
2916 out+=
"@iverbatim\n";
2918 std::string location;
2923 while (
end<=size && data[
end-1]!=
'\n')
end++;
2926 while (j<
end && data[j]==
' ') j++,indent++;
2936 while (emptyLines>0)
2944 std::string lineLoc;
2949 out+=data.substr(offset,
end-offset);
2957 out+=
"@endiverbatim";
2958 if (!location.empty())
2966 while (emptyLines>0)
2982 const size_t size = data.size();
2983 size_t nb=0,
end=offset+1, j=0;
2988 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
2989 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
2996 size_t l = endBlockName.
length();
2997 for (;
end+l+1<size;
end++)
2999 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3000 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3014 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3015 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3018 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3019 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3030 else if (nb==0 && data[
end-1]==
'`')
3032 while (
end<=size && data[
end-1]==
'`')
end++,nb++;
3034 else if (nb>0 && data[
end-1]==
'`')
3037 while (
end<=size && data[
end-1]==
'`')
end++,enb++;
3051 size_t blockStart,
size_t blockEnd)
3054 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3055 const size_t size=data.size();
3057 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3078 size_t pi=std::string::npos;
3079 bool newBlock =
false;
3080 bool insideList =
false;
3081 size_t currentIndent = refIndent;
3082 size_t listIndent = refIndent;
3083 const size_t size = data.size();
3090 size_t lineIndent=0;
3091 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3097 if (insideList && lineIndent<currentIndent)
3100 currentIndent = refIndent;
3108 if (listIndent<currentIndent+4)
3112 currentIndent = listIndent;
3119 currentIndent = listIndent;
3128 if (pi!=std::string::npos)
3130 size_t blockStart=0, blockEnd=0, blockOffset=0;
3131 if (
isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3133 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3135 size_t cmdPos = pi+blockStart+1;
3136 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3142 if (pl[ii]==
'\n') nl++;
3145 bool addNewLines =
false;
3147 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3156 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3157 addNewLines =
false;
3172 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3174 if (addNewLines)
out+=
'\n';
3179 addSpecialCommand(
"startuml",
"enduml");
3183 addSpecialCommand(
"dot",
"enddot");
3185 else if (lang==
"msc")
3187 addSpecialCommand(
"msc",
"endmsc");
3194 pi=std::string::npos;
3198 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3201 pi=std::string::npos;
3208 out+=data.substr(pi,i-pi);
3214 if (pi!=std::string::npos && pi<size)
3222 out+=data.substr(pi);
3236 size_t pi = std::string::npos;
3252 size_t currentIndent = indent;
3253 size_t listIndent = indent;
3254 bool insideList =
false;
3255 bool newBlock =
false;
3258 while (i<data.size())
3263 size_t lineIndent=0;
3265 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3271 if (insideList && lineIndent<currentIndent)
3274 currentIndent = indent;
3282 if (listIndent<currentIndent+4)
3286 currentIndent = listIndent;
3293 currentIndent = listIndent;
3305 if (pi!=std::string::npos)
3307 size_t blockStart=0, blockEnd=0, blockOffset=0;
3309 size_t blockIndent = currentIndent;
3313 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3317 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3327 size_t l = endBlockName.
length();
3328 while (i+l<data.size())
3330 if ((data[i]==
'\\' || data[i]==
'@') &&
3331 data[i-1]!=
'\\' && data[i-1]!=
'@')
3348 while (pi<data.size() && data[pi]==
' ') pi++;
3349 QCString header = data.substr(pi,i-pi-1);
3356 out+=level==1?
"@section ":
"@subsection ";
3364 out+=level==1?
"<h1>":
"<h2>";
3366 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3373 pi=std::string::npos;
3378 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3386 else if (
isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3392 pi=std::string::npos;
3400 pi=std::string::npos;
3407 pi=std::string::npos;
3420 if (pi!=std::string::npos && pi<data.size())
3422 if (
isLinkRef(data.substr(pi),
id,link,title))
3439#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3440 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3443 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3444 OPC(idlexcept);
OPC(file);
3454 std::string_view data(docs.
str());
3455 const size_t size = data.size();
3458 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3465 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3471 (data[i]==
'\\' || data[i]==
'@') &&
3486 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3504 std::string_view data(docs_org.
str());
3505 const size_t size = data.size();
3507 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3509 if (data[i]==
'\n') prepend++;
3512 if (i>=size) {
return QCString(); }
3514 while (end1<size && data[end1-1]!=
'\n') end1++;
3521 while (end2<size && data[end2-1]!=
'\n') end2++;
3522 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3524 title = data.substr(i,end1-i-1);
3525 docs+=
"\n\n"+docs_org.
mid(end2);
3526 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3532 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3535 docs+=docs_org.
mid(end1);
3540 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3551 if (input.
isEmpty())
return input;
3556 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3557 s =
detab(s,refIndent);
3561 s =
prv->processQuotations(s.
view(),refIndent);
3565 s =
prv->processBlocks(s.
view(),refIndent);
3583 const char *p = result.
data();
3586 while (*p==
' ') p++;
3587 while (*p==
'\n') {startNewlines++;p++;};
3590 if (p>result.
data())
3593 result = result.
mid(
static_cast<int>(p-result.
data()));
3606 if (i!=-1) baseFn = baseFn.
left(i);
3630 const char *fileBuf,
3631 const std::shared_ptr<Entry> &root,
3634 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3636 current->lang = SrcLangExt::Markdown;
3637 current->fileName = fileName;
3638 current->docFile = fileName;
3639 current->docLine = 1;
3645 bool isIdGenerated =
false;
3653 int indentLevel=title.
isEmpty() ? 0 : -1;
3660 bool wasEmpty =
id.isEmpty();
3661 if (wasEmpty)
id = mdFileNameId;
3667 if (!mdfileAsMainPage.
isEmpty() &&
3671 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3672 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3674 else if (
id==
"mainpage" ||
id==
"index")
3676 if (title.
isEmpty()) title = titleFn;
3677 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3678 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3680 else if (isSubdirDocs)
3684 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3686 docs.
prepend(
"@dir\\ilinebr ");
3697 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3699 else if (!generatedId.
isEmpty())
3701 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3703 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3706 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3708 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3710 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3715 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3717 std::string s = docs.str();
3720 QCString orgLabel = match[1].str();
3721 QCString orgTitle = match[2].str();
3724 docs = docs.
left(match[1].position())+
3727 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3739 p->commentScanner.enterFile(fileName,lineNr);
3741 bool needsEntry =
false;
3745 while (
p->commentScanner.parseCommentBlock(
3763 QCString docFile = current->docFile;
3764 root->moveToSubEntryAndRefresh(current);
3765 current->lang = SrcLangExt::Markdown;
3766 current->docFile = docFile;
3767 current->docLine = lineNr;
3772 root->moveToSubEntryAndKeep(current);
3774 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.
std::unique_ptr< Private > prv
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)
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.
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 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 bool isEndOfList(std::string_view data)
static size_t computeIndentExcludingListMarkers(std::string_view data)
static Alignment markersToAlignment(bool leftMarker, bool rightMarker)
helper function to convert presence of left and/or right alignment markers to an alignment value
static QCString escapeSpecialChars(const QCString &s)
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.
const size_t codeBlockIndent
static ExplicitPageResult isExplicitPage(const QCString &docs)
#define ignoreCloseEmphChar(c, cn)
static const std::unordered_map< std::string, std::string > g_quotationHeaderMap
#define isOpenEmphChar(c)
static bool isFencedCodeBlock(std::string_view data, size_t refIndent, QCString &lang, size_t &start, size_t &end, size_t &offset)
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 bool isTableBlock(std::string_view data)
Returns TRUE iff data points to the start of a table block.
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)
std::function< int(std::string_view, size_t)> Action_t
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)
int processNmdash(std::string_view data, size_t offset)
Process ndash and mdashes.
void writeOneLineHeaderOrRuler(std::string_view data)
std::array< Action_t, 256 > actions
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.