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=='%' || \
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);
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)
1676 size_t f_begin = nb;
1677 while (f_begin <
end && data[f_begin]==
' ')
1681 size_t f_end =
end - nb;
1682 while (f_end > nb && data[f_end-1]==
' ')
1690 if (f_begin < f_end)
1692 QCString codeFragment = data.substr(f_begin, f_end-f_begin);
1698 return static_cast<int>(
end);
1717 const size_t size = data.size();
1723 size_t l = endBlockName.
length();
1726 if ((data[i]==
'\\' || data[i]==
'@') &&
1727 data[i-1]!=
'\\' && data[i-1]!=
'@')
1734 return static_cast<int>(i+1+l);
1743 out+=data.substr(0,endPos);
1744 return static_cast<int>(endPos);
1746 if (size>1 && data[0]==
'\\')
1749 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1755 else if (c==
'\\' || c==
'@')
1757 out+=data.substr(0,2);
1761 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1763 out+=data.substr(1,3);
1767 else if (c==
'-' && size>2 && data[2]==
'-')
1769 out+=data.substr(1,2);
1774 else if (size>1 && data[0]==
'@')
1777 if (c==
'\\' || c==
'@')
1779 out+=data.substr(0,2);
1793 const size_t size = data.size();
1797 while (
end<size && ((action=
actions[
static_cast<uint8_t
>(data[
end])])==
nullptr))
end++;
1799 out+=data.substr(i,
end-i);
1800 if (
end>=size)
break;
1803 int iend = action(data.substr(i),i);
1821 const size_t size = data.size();
1822 while (i<size && data[i]==
' ') i++;
1823 if (i==size)
return 0;
1828 while (i<size && data[i]==
'=') i++,c++;
1829 while (i<size && data[i]==
' ') i++;
1830 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1831 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1846 while (i<size && data[i]==
'-') i++,c++;
1847 while (i<size && data[i]==
' ') i++;
1848 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1858 const size_t size = data.size();
1859 while (i<size && data[i]==
' ') i++;
1864 while (i<size && (data[i]==
'>' || data[i]==
' '))
1866 if (data[i]==
'>') level++;
1871 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1886 const size_t size = data.size();
1889 while (i<size && data[i]==
' ') i++;
1890 if (i>=size || data[i]!=
'[') {
return 0; }
1892 size_t refIdStart=i;
1893 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1894 if (i>=size || data[i]!=
']') {
return 0; }
1895 refid = data.substr(refIdStart,i-refIdStart);
1896 if (refid.
isEmpty()) {
return 0; }
1900 if (i>=size || data[i]!=
':') {
return 0; }
1904 while (i<size && data[i]==
' ') i++;
1905 if (i<size && data[i]==
'\n')
1908 while (i<size && data[i]==
' ') i++;
1910 if (i>=size) {
return 0; }
1912 if (i<size && data[i]==
'<') i++;
1914 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
1916 if (i<size && data[i]==
'>') i++;
1917 if (linkStart==linkEnd) {
return 0; }
1918 link = data.substr(linkStart,linkEnd-linkStart);
1920 if (link==
"@ref" || link==
"\\ref")
1923 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
1924 link+=data.substr(argStart,i-argStart);
1931 while (i<size && data[i]==
' ') i++;
1932 if (i<size && data[i]==
'\n')
1936 while (i<size && data[i]==
' ') i++;
1940 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
1945 if (c==
'\'' || c==
'"' || c==
'(')
1950 size_t titleStart=i;
1952 while (i<size && data[i]!=
'\n') i++;
1957 while (
end>titleStart && data[
end]!=c)
end--;
1960 title = data.substr(titleStart,
end-titleStart);
1964 while (i<size && data[i]==
' ') i++;
1976 size_t size = data.size();
1977 if (size>0 && data[size-1]==
'\n') size--;
1978 while (i<size && data[i]==
' ') i++;
1979 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
1981 if (c!=
'*' && c!=
'-' && c!=
'_')
1993 else if (data[i]!=
' ')
2008 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2010 std::string ti = title.str();
2013 std::string
id = match[1].str();
2014 title = title.
left(match.position());
2017 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2023 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2026 if (pIsIdGenerated) *pIsIdGenerated=
true;
2041 int level = 0, blanks=0;
2042 const size_t size = data.size();
2045 while (i<size && data[i]==
' ') i++;
2046 if (i>=size || data[i]!=
'#')
2050 while (i<size && data[i]==
'#') i++,level++;
2055 while (i<size && data[i]==
' ') i++,blanks++;
2056 if (level==1 && blanks==0)
2063 while (
end<size && data[
end]!=
'\n')
end++;
2064 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2067 header = data.substr(i,
end-i);
2071 int idx=
static_cast<int>(header.
length())-1;
2072 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2073 header=header.
left(idx+1);
2076 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2107 while (i<data.size())
2118 (data[(i)]=='<' && \
2119 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2120 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2129 const size_t size=data.size();
2133 bool listMarkerSkipped=
FALSE;
2136 (!listMarkerSkipped &&
2137 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2138 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2139 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2140 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2149 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2153 if (j+1<size && data[j+1]==
' ')
2155 listMarkerSkipped=
TRUE;
2172 listMarkerSkipped=
TRUE;
2174 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2176 listMarkerSkipped=
TRUE;
2180 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2182 listMarkerSkipped=
TRUE;
2184 if (data[i]!=
' ' && !listMarkerSkipped)
2197 size_t normalIndent = 0;
2198 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2200 size_t result = listIndent>normalIndent ? listIndent : 0;
2211 while (i<data.size())
2217 else if (data[i]==
'\n')
2221 else if (data[i]!=
' ' && data[i]!=
'\t')
2233 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset)
2236 const char dot =
'.';
2237 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2238 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2239 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2245 const size_t size = data.size();
2246 while (i<size && data[i]==
' ') indent++,i++;
2247 if (indent>=refIndent+4)
2249 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2253 if (i<size && data[i]==
'`') tildaChar=
'`';
2254 while (i<size && data[i]==tildaChar) startTildes++,i++;
2257 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2260 if (i<size && data[i]==
'{')
2263 if (data[i] == dot) i++;
2265 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2266 if (i<size && data[i]==
'}')
2268 lang = data.substr(startLang,i-startLang);
2277 else if (i<size && isLangChar(data[i]))
2279 if (data[i] == dot) i++;
2281 if (i<size && isAlphaChar(data[i]))
2284 while (i<size && isAlphaNChar(data[i])) i++;
2286 lang = data.substr(startLang,i-startLang);
2296 if (data[i]==tildaChar)
2300 while (i<size && data[i]==tildaChar) endTildes++,i++;
2301 while (i<size && data[i]==
' ') i++;
2303 if (endTildes==startTildes)
2306 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2317static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2324 const size_t size = data.size();
2325 while (i<size && data[i]==
' ') indent0++,i++;
2329 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2332 if (indent0>=size || data[indent0]==
'\n')
2334 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2341 int offset_i =
static_cast<int>(offset);
2345 int j =
static_cast<int>(i)-offset_i-1;
2347 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2350 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2356 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2367 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2376 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2382 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2389 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2397 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2413 const size_t size = data.size();
2416 while (i<size && data[i]==
' ') i++;
2417 if (i<size && data[i]==
'|' && data[i]!=
'\n') i++,n++;
2422 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2425 if (j>0 && i>0) i--;
2426 while (i>0 && data[i]==
' ') i--;
2427 if (i>0 && data[i-1]!=
'\\' && data[i]==
'|') i--,n++;
2437 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2438 if (columns==1) columns++;
2442 if (n==2 && columns==0)
2454 size_t cc0=0, start=0,
end=0;
2458 if (i>=data.size() || cc0<1)
2470 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2479 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2494 const size_t size = data.size();
2496 size_t columns=0, start=0,
end=0;
2498 size_t headerStart = start;
2499 size_t headerEnd =
end;
2505 std::vector<int> columnAlignment(columns);
2507 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2513 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2514 if (data[j]==
'-') startFound=
TRUE;
2517 if (data[j]==
'-') rightMarker=
FALSE;
2518 else if (data[j]==
':') rightMarker=
TRUE;
2519 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2543 std::vector<std::vector<TableCell> > tableContents;
2545 size_t m = headerStart;
2546 std::vector<TableCell> headerContents(columns);
2547 for (k=0;k<columns;k++)
2549 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2551 headerContents[k].cellText += data[m++];
2556 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2557 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2559 tableContents.push_back(headerContents);
2565 if (cc!=columns)
break;
2569 std::vector<TableCell> rowContents(columns);
2572 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2576 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2577 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2582 rowContents[k].cellText += data[j];
2588 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2589 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2590 tableContents.push_back(rowContents);
2596 out+=
"<table class=\"markdownTable\">";
2597 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2598 for (
size_t row = 0; row < tableContents.size(); row++)
2604 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2608 out+=
"\n<tr class=\"markdownTableRowEven\">";
2613 out+=
"\n <tr class=\"markdownTableHead\">";
2615 for (
size_t c = 0; c < columns; c++)
2618 QCString cellText(tableContents[row][c].cellText);
2624 if (tableContents[row][c].cellText ==
"^")
2628 if (tableContents[row][c].colSpan)
2630 int cr =
static_cast<int>(c);
2631 while ( cr >= 0 && tableContents[row][cr].colSpan)
2635 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2637 size_t rowSpan = 1, spanRow = row+1;
2638 while ((spanRow < tableContents.size()) &&
2639 (tableContents[spanRow][c].cellText ==
"^"))
2645 out+=
" <" + cellTag +
" " + cellClass;
2647 switch (columnAlignment[c])
2659 out+=
" rowspan=\"" + spanStr +
"\"";
2665 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2674 out+=
" colspan=\"" + spanStr +
"\"";
2678 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2681 cellClass =
"class=\"markdownTableBody";
2697 while (i<data.size() && data[i]!=
'\n')
2699 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2702 if (i>=data.size()) {
return 0; }
2703 if (i<2) {
return 0; }
2704 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2744 out+=
"</"+hTag+
">\n";
2747 else if (data.size()>0)
2749 size_t tmpSize = data.size();
2750 if (data[data.size()-1] ==
'\n') tmpSize--;
2751 out+=data.substr(0,tmpSize);
2755 out+=
"\\ilinebr<br>";
2757 if (tmpSize != data.size())
out+=
'\n';
2763 {
"[!note]",
"\\note" },
2764 {
"[!warning]",
"\\warning" },
2765 {
"[!tip]",
"\\remark" },
2766 {
"[!caution]",
"\\attention" },
2767 {
"[!important]",
"\\important" }
2776 const size_t size = data.size();
2777 std::string startCmd;
2778 int isGitHubAlert =
false;
2779 int isGitHubFirst =
false;
2784 while (
end<=size && data[
end-1]!=
'\n')
end++;
2789 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2791 if (data[j]==
'>') { level++; indent=j+1; }
2792 else if (j>0 && data[j-1]==
'>') indent=j+1;
2795 if (indent>0 && j>0 && data[j-1]==
'>' &&
2796 !(j==size || data[j]==
'\n'))
2813 isGitHubAlert =
true;
2814 isGitHubFirst =
true;
2815 startCmd = it->second;
2820 if (level!=1 || !isGitHubAlert)
2822 for (
int l=curLevel;l<level-1;l++)
2824 out+=
"<blockquote>";
2826 out +=
"<blockquote>‍";
2828 else if (!startCmd.empty())
2830 out += startCmd +
" ";
2833 else if (level<curLevel)
2836 if (level==0 && isGitHubAlert)
2842 out +=
"</blockquote>\\ilinebr ";
2851 if (curLevel!=0 || !isGitHubAlert)
2853 std::string_view txt = data.substr(indent,
end-indent);
2856 if (!isGitHubFirst)
out +=
"<br>";
2863 isGitHubFirst =
false;
2878 for (
int l=0;l<curLevel;l++)
2880 out+=
"</blockquote>";
2891 size_t size = data.size();
2892 while (i<data.size() && data[i]==
' ') i++;
2895 size_t locStart = i;
2896 if (i>offset) locStart--;
2899 while (i+9<size && data[i]!=
'\n')
2911 location=data.substr(locStart,i-locStart);
2913 while (indent>0 && i<size && data[i]==
' ') i++,indent--;
2914 if (i<size && data[i]==
'\n') i++;
2925 const size_t size = data.size();
2928 out+=
"@iverbatim\n";
2930 std::string location;
2935 while (
end<=size && data[
end-1]!=
'\n')
end++;
2938 while (j<
end && data[j]==
' ') j++,indent++;
2948 while (emptyLines>0)
2956 std::string lineLoc;
2961 out+=data.substr(offset,
end-offset);
2969 out+=
"@endiverbatim";
2970 if (!location.empty())
2978 while (emptyLines>0)
2994 const size_t size = data.size();
2995 size_t nb=0,
end=offset+1, j=0;
3000 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3001 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3008 size_t l = endBlockName.
length();
3009 for (;
end+l+1<size;
end++)
3011 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3012 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3026 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3027 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3030 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3031 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3042 else if (nb==0 && data[
end-1]==
'`')
3044 while (
end<=size && data[
end-1]==
'`')
end++,nb++;
3046 else if (nb>0 && data[
end-1]==
'`')
3049 while (
end<=size && data[
end-1]==
'`')
end++,enb++;
3063 size_t blockStart,
size_t blockEnd)
3066 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3067 const size_t size=data.size();
3069 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3090 size_t pi=std::string::npos;
3091 bool newBlock =
false;
3092 bool insideList =
false;
3093 size_t currentIndent = refIndent;
3094 size_t listIndent = refIndent;
3095 const size_t size = data.size();
3102 size_t lineIndent=0;
3103 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3109 if (insideList && lineIndent<currentIndent)
3112 currentIndent = refIndent;
3120 if (listIndent<currentIndent+4)
3124 currentIndent = listIndent;
3131 currentIndent = listIndent;
3140 if (pi!=std::string::npos)
3142 size_t blockStart=0, blockEnd=0, blockOffset=0;
3143 if (
isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3145 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3147 size_t cmdPos = pi+blockStart+1;
3148 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3154 if (pl[ii]==
'\n') nl++;
3157 bool addNewLines =
false;
3159 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3168 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3169 addNewLines =
false;
3184 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3186 if (addNewLines)
out+=
'\n';
3191 addSpecialCommand(
"startuml",
"enduml");
3195 addSpecialCommand(
"dot",
"enddot");
3197 else if (lang==
"msc")
3199 addSpecialCommand(
"msc",
"endmsc");
3206 pi=std::string::npos;
3210 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3213 pi=std::string::npos;
3220 out+=data.substr(pi,i-pi);
3226 if (pi!=std::string::npos && pi<size)
3234 out+=data.substr(pi);
3248 size_t pi = std::string::npos;
3264 size_t currentIndent = indent;
3265 size_t listIndent = indent;
3266 bool insideList =
false;
3267 bool newBlock =
false;
3270 while (i<data.size())
3275 size_t lineIndent=0;
3277 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3283 if (insideList && lineIndent<currentIndent)
3286 currentIndent = indent;
3294 if (listIndent<currentIndent+4)
3298 currentIndent = listIndent;
3305 currentIndent = listIndent;
3317 if (pi!=std::string::npos)
3319 size_t blockStart=0, blockEnd=0, blockOffset=0;
3321 size_t blockIndent = currentIndent;
3325 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3329 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3339 size_t l = endBlockName.
length();
3340 while (i+l<data.size())
3342 if ((data[i]==
'\\' || data[i]==
'@') &&
3343 data[i-1]!=
'\\' && data[i-1]!=
'@')
3360 while (pi<data.size() && data[pi]==
' ') pi++;
3361 QCString header = data.substr(pi,i-pi-1);
3368 out+=level==1?
"@section ":
"@subsection ";
3376 out+=level==1?
"<h1>":
"<h2>";
3378 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3385 pi=std::string::npos;
3390 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3398 else if (
isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3404 pi=std::string::npos;
3412 pi=std::string::npos;
3419 pi=std::string::npos;
3432 if (pi!=std::string::npos && pi<data.size())
3434 if (
isLinkRef(data.substr(pi),
id,link,title))
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 &&
3487 (data[i]==
'\\' || data[i]==
'@') &&
3507 std::string_view data(docs_org.
str());
3508 const size_t size = data.size();
3510 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3512 if (data[i]==
'\n') prepend++;
3515 if (i>=size) {
return QCString(); }
3517 while (end1<size && data[end1-1]!=
'\n') end1++;
3524 while (end2<size && data[end2-1]!=
'\n') end2++;
3525 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3527 title = data.substr(i,end1-i-1);
3528 docs+=
"\n\n"+docs_org.
mid(end2);
3529 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3535 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3538 docs+=docs_org.
mid(end1);
3543 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3554 if (input.
isEmpty())
return input;
3559 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3560 s =
detab(s,refIndent);
3564 s =
prv->processQuotations(s.
view(),refIndent);
3568 s =
prv->processBlocks(s.
view(),refIndent);
3586 const char *p = result.
data();
3589 while (*p==
' ') p++;
3590 while (*p==
'\n') {startNewlines++;p++;};
3593 if (p>result.
data())
3596 result = result.
mid(
static_cast<int>(p-result.
data()));
3609 if (i!=-1) baseFn = baseFn.
left(i);
3633 const char *fileBuf,
3634 const std::shared_ptr<Entry> &root,
3637 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3640 current->fileName = fileName;
3641 current->docFile = fileName;
3642 current->docLine = 1;
3647 bool isIdGenerated =
false;
3655 int indentLevel=title.
isEmpty() ? 0 : -1;
3662 bool wasEmpty =
id.isEmpty();
3663 if (wasEmpty)
id = mdFileNameId;
3669 if (!mdfileAsMainPage.
isEmpty() &&
3673 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3674 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3676 else if (
id==
"mainpage" ||
id==
"index")
3678 if (title.
isEmpty()) title = titleFn;
3679 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3680 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3682 else if (isSubdirDocs)
3686 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3688 docs.
prepend(
"@dir\\ilinebr ");
3699 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3701 else if (!generatedId.
isEmpty())
3703 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3705 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3708 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3710 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3712 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3717 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3719 std::string s = docs.str();
3722 QCString orgLabel = match[1].str();
3723 QCString orgTitle = match[2].str();
3726 docs = docs.
left(match[1].position())+
3729 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3741 p->commentScanner.enterFile(fileName,lineNr);
3743 bool needsEntry =
false;
3747 while (
p->commentScanner.parseCommentBlock(
3765 QCString docFile = current->docFile;
3766 root->moveToSubEntryAndRefresh(current);
3768 current->docFile = docFile;
3769 current->docLine = lineNr;
3774 root->moveToSubEntryAndKeep(current);
3776 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 hasLineBreak(std::string_view data)
@ explicitDirPage
docs start with a dir command
@ explicitMainPage
docs start with a mainpage command
@ explicitPage
docs start with a page command
@ notExplicit
docs doesn't start with either page or mainpage
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
Protection
Protection level of members.
SrcLangExt
Language as given by extension.
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.