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;
212 if (data[0] ==
'\\' &&
qstrncmp(data.data()+1,
"ilinebr ",7)==0)
return data[8]==
' ' ? 9 : 8;
222 const char *p=s.
data();
226 if (c==
'"' && pc!=
'\\') result+=
'\\';
239 bool insideQuote=
FALSE;
241 const char *p=s.
data();
248 if (pc!=
'\\') { insideQuote=!insideQuote; }
258 if ((p[0]==
':') && (p[1]==
':'))
270 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
271 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
274 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
275 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
276 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
291 if (leftMarker && rightMarker)
299 else if (rightMarker)
314 for (
const auto &attr_ : attrList)
317 int i = attr.
find(
':');
324 return attr.
mid(i+1);
361 using EndBlockFunc =
QCString (*)(
const std::string &,bool,char);
363 static const auto getEndBlock = [](
const std::string &blockName,bool,char) ->
QCString
365 return "end"+blockName;
367 static const auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
QCString
369 return openBracket ?
QCString(
"}") :
"end"+blockName;
371 static const auto getEndUml = [](
const std::string &,bool,char) ->
QCString
375 static const auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
QCString
379 case '$':
return "f$";
380 case '(':
return "f)";
381 case '[':
return "f]";
382 case '{':
return "f}";
388 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
390 {
"dot", getEndBlock },
391 {
"code", getEndCode },
392 {
"icode", getEndBlock },
393 {
"msc", getEndBlock },
394 {
"verbatim", getEndBlock },
395 {
"iverbatim", getEndBlock },
396 {
"iliteral", getEndBlock },
397 {
"latexonly", getEndBlock },
398 {
"htmlonly", getEndBlock },
399 {
"xmlonly", getEndBlock },
400 {
"rtfonly", getEndBlock },
401 {
"manonly", getEndBlock },
402 {
"docbookonly", getEndBlock },
403 {
"startuml", getEndUml },
404 {
"f", getEndFormula }
407 const size_t size = data.
size();
408 bool openBracket = offset>0 && data.data()[-1]==
'{';
409 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
413 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
415 std::string blockName(data.substr(1,
end-1));
416 auto it = blockNames.find(blockName);
418 if (it!=blockNames.end())
420 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
430 using EndCmdFunc = size_t (*)(std::string_view,size_t);
432 static const auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
437 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
439 if (c==
'\\') lc=
'\\';
440 else if (c!=
' ') lc=0;
446 static const auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
448 if (offset_<data_.size() && data_[offset_]==
' ')
456 while (offset_<data_.size() && data_[offset_]==
' ')
461 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
466 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
468 size_t off = offset_;
469 while (off<data_.size() && data_[off]==
' ')
473 if (off<data_.size() && data_[off]==
',')
492 static const auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
494 return endOfLabels(data_,offset_,
false);
497 static const auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
499 size_t index=offset_;
500 if (index<data_.size() && data_[index]==
' ')
503 while (index<data_.size() && data_[index]==
' ') index++;
505 if (index<data_.size() && data_[index]==
'{')
509 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
510 if (index==data_.size() || data_[index]!=
'}')
return 0;
513 return endOfLabel(data_,offset_);
516 static const auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
518 size_t index=offset_;
519 if (index<data_.size() && data_[index]==
' ')
522 while (index<data_.size() && data_[index]==
' ') index++;
524 if (index<data_.size() && data_[index]==
'[')
528 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
529 if (index==data_.size() || data_[index]!=
']')
return 0;
532 return endOfLabels(data_,offset_,
true);
535 static const auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
537 return endOfLabels(data_,offset_,
true);
540 static const auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
542 if (offset_<data_.size() && data_[offset_]==
' ')
547 while (offset_<data_.size() && data_[offset_]==
' ')
552 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
560 while (offset_<data_.size() && (c=data_[offset_++]))
563 else if (c==
')') count--;
564 if (count==0)
return offset_;
572 static const auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
574 return endOfFuncLike(data_,offset_,
true);
577 static const auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
579 return endOfFuncLike(data_,offset_,
false);
582 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
585 {
"addindex", endOfLine },
586 {
"addtogroup", endOfLabel },
587 {
"anchor", endOfLabel },
590 {
"category", endOfLine },
591 {
"cite", endOfLabel },
592 {
"class", endOfLine },
593 {
"concept", endOfLine },
594 {
"copybrief", endOfFunc },
595 {
"copydetails", endOfFunc },
596 {
"copydoc", endOfFunc },
597 {
"def", endOfFunc },
598 {
"defgroup", endOfLabel },
599 {
"diafile", endOfLine },
600 {
"dir", endOfLine },
601 {
"dockbookinclude",endOfLine },
602 {
"dontinclude", endOfLine },
603 {
"dotfile", endOfLine },
605 {
"elseif", endOfGuard },
606 {
"em", endOfLabel },
607 {
"emoji", endOfLabel },
608 {
"enum", endOfLabel },
609 {
"example", endOfLine },
610 {
"exception", endOfLine },
611 {
"extends", endOfLabel },
612 {
"file", endOfLine },
614 {
"headerfile", endOfLine },
615 {
"htmlinclude", endOfLine },
616 {
"ianchor", endOfLabelOpt },
617 {
"idlexcept", endOfLine },
618 {
"if", endOfGuard },
619 {
"ifnot", endOfGuard },
620 {
"image", endOfLine },
621 {
"implements", endOfLine },
622 {
"include", endOfLine },
623 {
"includedoc", endOfLine },
624 {
"includelineno", endOfLine },
625 {
"ingroup", endOfLabel },
626 {
"interface", endOfLine },
627 {
"latexinclude", endOfLine },
628 {
"maninclude", endOfLine },
629 {
"memberof", endOfLabel },
630 {
"mscfile", endOfLine },
631 {
"namespace", endOfLabel },
632 {
"noop", endOfLine },
633 {
"overload", endOfLine },
635 {
"package", endOfLabel },
636 {
"page", endOfLabel },
637 {
"paragraph", endOfLabel },
638 {
"param", endOfParam },
639 {
"property", endOfLine },
640 {
"protocol", endOfLine },
641 {
"qualifier", endOfLine },
642 {
"ref", endOfLabel },
643 {
"refitem", endOfLine },
644 {
"related", endOfLabel },
645 {
"relatedalso", endOfLabel },
646 {
"relates", endOfLabel },
647 {
"relatesalso", endOfLabel },
648 {
"retval", endOfRetVal},
649 {
"rtfinclude", endOfLine },
650 {
"section", endOfLabel },
651 {
"skip", endOfLine },
652 {
"skipline", endOfLine },
653 {
"snippet", endOfLine },
654 {
"snippetdoc", endOfLine },
655 {
"snippetlineno", endOfLine },
656 {
"struct", endOfLine },
657 {
"subpage", endOfLabel },
658 {
"subparagraph", endOfLabel },
659 {
"subsubparagraph",endOfLabel },
660 {
"subsection", endOfLabel },
661 {
"subsubsection", endOfLabel },
662 {
"throw", endOfLabel },
663 {
"throws", endOfLabel },
664 {
"tparam", endOfLabel },
665 {
"typedef", endOfLine },
666 {
"plantumlfile", endOfLine },
667 {
"union", endOfLine },
668 {
"until", endOfLine },
669 {
"var", endOfLine },
670 {
"verbinclude", endOfLine },
671 {
"weakgroup", endOfLabel },
672 {
"xmlinclude", endOfLine },
673 {
"xrefitem", endOfLabel }
676 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
677 if (isEscaped)
return 0;
679 const size_t size = data.size();
681 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
682 if (
end==1)
return 0;
683 std::string cmdName(data.substr(1,
end-1));
685 auto it = cmdNames.find(cmdName);
686 if (it!=cmdNames.end())
689 result = it->second(data,
end);
702 const size_t size = data.size();
706 while (i<size && data[i]!=c &&
707 data[i]!=
'\\' && data[i]!=
'@' &&
708 !(data[i]==
'/' && data[i-1]==
'<') &&
728 while (i+len<size && data[i+len]==c)
735 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
741 return static_cast<int>(i);
749 while (i<size && data[i]==
'`') snb++,i++;
753 while (i<size && enb<snb)
755 if (data[i]==
'`') enb++;
756 if (snb==1 && data[i]==
'\'')
break;
760 else if (data[i]==
'@' || data[i]==
'\\')
766 size_t l = endBlockName.
length();
769 if ((data[i]==
'\\' || data[i]==
'@') &&
770 data[i-1]!=
'\\' && data[i-1]!=
'@')
780 else if (i+1<size &&
isIdChar(data[i+1]))
789 else if (data[i-1]==
'<' && data[i]==
'/')
793 else if (data[i]==
'\n')
796 while (i<size && data[i]==
' ') i++;
797 if (i>=size || data[i]==
'\n')
815 const size_t size = data.size();
818 if (size>1 && data[0]==c && data[1]==c) { i=1; }
823 if (len==0) {
return 0; }
825 if (i>=size) {
return 0; }
827 if (i+1<size && data[i+1]==c)
832 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
838 return static_cast<int>(i+1);
849 const size_t size = data.size();
859 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
861 if (c ==
'~')
out+=
"<strike>";
862 else out+=
"<strong>";
864 if (c ==
'~')
out+=
"</strike>";
865 else out+=
"</strong>";
867 return static_cast<int>(i+2);
881 const size_t size = data.size();
893 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
898 if (i+2<size && data[i+1]==c && data[i+2]==c)
902 out+=
"</strong></em>";
904 return static_cast<int>(i+3);
906 else if (i+1<size && data[i+1]==c)
917 return static_cast<int>(len - 2);
931 return static_cast<int>(len - 1);
942 const size_t size = data.size();
946 if (i<size && data[i]==
'-')
950 if (i<size && data[i]==
'-')
954 if (i<size && data[i]==
'-')
958 if (count>=2 && offset>=2 &&
qstrncmp(data.data()-2,
"<!",2)==0)
960 if (count==2 && size > 2 && data[2]==
'>')
962 if (count==3 && size > 3 && data[3]==
'>')
964 if (count==2 && (offset<8 ||
qstrncmp(data.data()-8,
"operator",8)!=0))
984 const size_t size = data.size();
987 while (i<size && data[i]!=
'"' && nl<2)
989 if (data[i]==
'\n') nl++;
992 if (i<size && data[i]==
'"' && nl<2)
994 out+=data.substr(0,i+1);
996 return static_cast<int>(i+1);
1008 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1010 const size_t size = data.size();
1016 while (i<size &&
isIdChar(data[i])) i++,l++;
1017 QCString tagName(data.substr(1,i-1));
1018 if (tagName.
lower()==
"pre")
1020 bool insideStr=
FALSE;
1024 if (!insideStr && c==
'<')
1026 if (data[i+1]==
'/' &&
1027 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1028 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1030 if (doWrite)
out+=data.substr(0,i+6);
1033 return static_cast<int>(i+6);
1036 else if (insideStr && c==
'"')
1038 if (data[i-1]!=
'\\') insideStr=
FALSE;
1051 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1054 if (doWrite)
out+=data.substr(0,i+2);
1056 return static_cast<int>(i+2);
1058 else if (data[i]==
'>')
1061 if (doWrite)
out+=data.substr(0,i+1);
1063 return static_cast<int>(i+1);
1065 else if (data[i]==
' ')
1068 bool insideAttr=
FALSE;
1071 if (!insideAttr && data[i]==
'"')
1075 else if (data[i]==
'"' && data[i-1]!=
'\\')
1079 else if (!insideAttr && data[i]==
'>')
1082 if (doWrite)
out+=data.substr(0,i+1);
1084 return static_cast<int>(i+1);
1104 const size_t size = data.size();
1116 if (size>2 && c!=
'~' && data[1]!=c)
1119 if (data[1]==
' ' || data[1]==
'\n' ||
1127 if (size>3 && data[1]==c && data[2]!=c)
1129 if (data[2]==
' ' || data[2]==
'\n' ||
1137 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1139 if (data[3]==
' ' || data[3]==
'\n' ||
1151 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1156 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1167 out+=link.
mid(fd ? 0 : 5);
1168 if (!explicitTitle && !content.
isEmpty())
1174 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1196 const size_t size = data.size();
1200 bool isImageLink =
FALSE;
1201 bool isImageInline =
FALSE;
1207 if (size<2 || data[1]!=
'[')
1216 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1218 if (data.data()[pos]==
'\n') numNLsNeeded--;
1219 else if (data.data()[pos]!=
' ')
1229 size_t contentStart=i;
1236 if (data[i-1]==
'\\')
1239 else if (data[i]==
'[')
1243 else if (data[i]==
']')
1246 if (level<=0)
break;
1248 else if (data[i]==
'\n')
1251 if (nl>1) {
return 0; }
1257 if (i>=size)
return 0;
1258 size_t contentEnd=i;
1259 content = data.substr(contentStart,contentEnd-contentStart);
1261 if (!isImageLink && content.
isEmpty()) {
return 0; }
1264 bool whiteSpace =
false;
1266 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1267 if (i<size && data[i]==
'\n')
1272 while (i<size && data[i]==
' ') i++;
1274 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1276 bool explicitTitle=
FALSE;
1277 if (i<size && data[i]==
'(')
1280 while (i<size && data[i]==
' ') i++;
1281 bool uriFormat=
false;
1282 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1285 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1290 if (nl>1) {
return 0; }
1292 else if (data[i]==
'(')
1296 else if (data[i]==
')')
1307 if (i>=size || data[i]==
'\n') {
return 0; }
1308 link = data.substr(linkStart,i-linkStart);
1311 if (link.
isEmpty()) {
return 0; }
1315 if (data[i]==
'\'' || data[i]==
'"')
1319 size_t titleStart=i;
1325 if (nl>1) {
return 0; }
1328 else if (data[i]==
'\\')
1332 else if (data[i]==c)
1343 size_t titleEnd = i-1;
1345 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1346 if (data[titleEnd]==c)
1348 title = data.substr(titleStart,titleEnd-titleStart);
1352 if (data[i]==
' ')i++;
1353 else if (data[i] ==
')')
break;
1367 else if (i<size && data[i]==
'[')
1373 while (i<size && data[i]!=
']')
1378 if (nl>1) {
return 0; }
1382 if (i>=size) {
return 0; }
1384 link = data.substr(linkStart,i-linkStart);
1396 link = lr_it->second.link;
1397 title = lr_it->second.title;
1407 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1414 link = lr_it->second.link;
1415 title = lr_it->second.title;
1419 else if (content==
"TOC")
1442 while (j<size && data[j]==
' ') { j++; }
1443 if (j<size && data[j]==
'{')
1448 size_t attributesStart=i;
1453 if (data[i-1]==
'\\')
1456 else if (data[i]==
'{')
1460 else if (data[i]==
'}')
1463 if (level<=0)
break;
1465 else if (data[i]==
'\n')
1468 if (nl>1) {
return 0; }
1473 if (i>=size)
return 0;
1474 size_t attributesEnd=i;
1475 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1484 while (pos<size && numNLsNeeded>0)
1486 if (data[pos]==
'\n') numNLsNeeded--;
1487 else if (data[pos]!=
' ')
1502 out+=
"@tableofcontents{html:";
1507 else if (isImageLink)
1511 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1516 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1517 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1518 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1519 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1520 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1574 if (explicitTitle && !title.
isEmpty())
1584 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1586 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1599 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1621 return static_cast<int>(i);
1628 const size_t size = data.size();
1632 while (nb<size && data[nb]==
'`')
1646 else if (data[
end]==
'\n')
1649 if (pc ==
'\n')
return 0;
1656 out+=data.substr(nb,
end-nb);
1658 return static_cast<int>(
end+1);
1662 if (data[
end]!=
' ') pc = data[
end];
1666 if (i < nb && end >= size)
1672 size_t f_begin = nb;
1673 while (f_begin <
end && data[f_begin]==
' ')
1677 size_t f_end =
end - nb;
1678 while (f_end > nb && data[f_end-1]==
' ')
1686 if (f_begin < f_end)
1688 QCString codeFragment = data.substr(f_begin, f_end-f_begin);
1694 return static_cast<int>(
end);
1713 const size_t size = data.size();
1719 size_t l = endBlockName.
length();
1722 if ((data[i]==
'\\' || data[i]==
'@') &&
1723 data[i-1]!=
'\\' && data[i-1]!=
'@')
1730 return static_cast<int>(i+1+l);
1739 out+=data.substr(0,endPos);
1740 return static_cast<int>(endPos);
1742 if (size>1 && data[0]==
'\\')
1745 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1751 else if (c==
'\\' || c==
'@')
1753 out+=data.substr(0,2);
1757 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1759 out+=data.substr(1,3);
1763 else if (c==
'-' && size>2 && data[2]==
'-')
1765 out+=data.substr(1,2);
1770 else if (size>1 && data[0]==
'@')
1773 if (c==
'\\' || c==
'@')
1775 out+=data.substr(0,2);
1789 const size_t size = data.size();
1793 while (
end<size && ((action=
actions[
static_cast<uint8_t
>(data[
end])])==
nullptr))
end++;
1795 out+=data.substr(i,
end-i);
1796 if (
end>=size)
break;
1799 int iend = action(data.substr(i),i);
1817 const size_t size = data.size();
1818 while (i<size && data[i]==
' ') i++;
1819 if (i==size)
return 0;
1824 while (i<size && data[i]==
'=') i++,c++;
1825 while (i<size && data[i]==
' ') i++;
1826 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1827 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1842 while (i<size && data[i]==
'-') i++,c++;
1843 while (i<size && data[i]==
' ') i++;
1844 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1854 const size_t size = data.size();
1855 while (i<size && data[i]==
' ') i++;
1860 while (i<size && (data[i]==
'>' || data[i]==
' '))
1862 if (data[i]==
'>') level++;
1867 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1882 const size_t size = data.size();
1885 while (i<size && data[i]==
' ') i++;
1886 if (i>=size || data[i]!=
'[') {
return 0; }
1888 size_t refIdStart=i;
1889 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1890 if (i>=size || data[i]!=
']') {
return 0; }
1891 refid = data.substr(refIdStart,i-refIdStart);
1892 if (refid.
isEmpty()) {
return 0; }
1896 if (i>=size || data[i]!=
':') {
return 0; }
1900 while (i<size && data[i]==
' ') i++;
1901 if (i<size && data[i]==
'\n')
1904 while (i<size && data[i]==
' ') i++;
1906 if (i>=size) {
return 0; }
1908 if (i<size && data[i]==
'<') i++;
1910 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
1912 if (i<size && data[i]==
'>') i++;
1913 if (linkStart==linkEnd) {
return 0; }
1914 link = data.substr(linkStart,linkEnd-linkStart);
1916 if (link==
"@ref" || link==
"\\ref")
1919 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
1920 link+=data.substr(argStart,i-argStart);
1927 while (i<size && data[i]==
' ') i++;
1928 if (i<size && data[i]==
'\n')
1932 while (i<size && data[i]==
' ') i++;
1936 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
1941 if (c==
'\'' || c==
'"' || c==
'(')
1946 size_t titleStart=i;
1948 while (i<size && data[i]!=
'\n') i++;
1953 while (
end>titleStart && data[
end]!=c)
end--;
1956 title = data.substr(titleStart,
end-titleStart);
1960 while (i<size && data[i]==
' ') i++;
1972 size_t size = data.size();
1973 if (size>0 && data[size-1]==
'\n') size--;
1974 while (i<size && data[i]==
' ') i++;
1975 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
1977 if (c!=
'*' && c!=
'-' && c!=
'_')
1989 else if (data[i]!=
' ')
2004 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2006 std::string ti = title.str();
2009 std::string
id = match[1].str();
2010 title = title.
left(match.position());
2013 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2019 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2022 if (pIsIdGenerated) *pIsIdGenerated=
true;
2037 int level = 0, blanks=0;
2038 const size_t size = data.size();
2041 while (i<size && data[i]==
' ') i++;
2042 if (i>=size || data[i]!=
'#')
2046 while (i<size && data[i]==
'#') i++,level++;
2051 while (i<size && data[i]==
' ') i++,blanks++;
2052 if (level==1 && blanks==0)
2059 while (
end<size && data[
end]!=
'\n')
end++;
2060 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2063 header = data.substr(i,
end-i);
2067 int idx=
static_cast<int>(header.
length())-1;
2068 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2069 header=header.
left(idx+1);
2072 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2103 while (i<data.size())
2114 (data[(i)]=='<' && \
2115 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2116 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2125 const size_t size=data.size();
2129 bool listMarkerSkipped=
FALSE;
2132 (!listMarkerSkipped &&
2133 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2134 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2135 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2136 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2145 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2149 if (j+1<size && data[j+1]==
' ')
2151 listMarkerSkipped=
TRUE;
2168 listMarkerSkipped=
TRUE;
2170 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2172 listMarkerSkipped=
TRUE;
2176 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2178 listMarkerSkipped=
TRUE;
2180 if (data[i]!=
' ' && !listMarkerSkipped)
2193 size_t normalIndent = 0;
2194 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2196 size_t result = listIndent>normalIndent ? listIndent : 0;
2207 while (i<data.size())
2213 else if (data[i]==
'\n')
2217 else if (data[i]!=
' ' && data[i]!=
'\t')
2229 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset)
2232 const char dot =
'.';
2233 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2234 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2235 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2241 const size_t size = data.size();
2242 while (i<size && data[i]==
' ') indent++,i++;
2243 if (indent>=refIndent+4)
2245 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2249 if (i<size && data[i]==
'`') tildaChar=
'`';
2250 while (i<size && data[i]==tildaChar) startTildes++,i++;
2253 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2256 if (i<size && data[i]==
'{')
2259 if (data[i] == dot) i++;
2261 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2262 if (i<size && data[i]==
'}')
2264 lang = data.substr(startLang,i-startLang);
2273 else if (i<size && isLangChar(data[i]))
2275 if (data[i] == dot) i++;
2277 if (i<size && isAlphaChar(data[i]))
2280 while (i<size && isAlphaNChar(data[i])) i++;
2282 lang = data.substr(startLang,i-startLang);
2292 if (data[i]==tildaChar)
2296 while (i<size && data[i]==tildaChar) endTildes++,i++;
2297 while (i<size && data[i]==
' ') i++;
2299 if (endTildes==startTildes)
2302 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2313static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2320 const size_t size = data.size();
2321 while (i<size && data[i]==
' ') indent0++,i++;
2325 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2328 if (indent0>=size || data[indent0]==
'\n')
2330 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2337 int offset_i =
static_cast<int>(offset);
2341 int j =
static_cast<int>(i)-offset_i-1;
2343 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2346 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2352 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2363 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2372 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2378 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2385 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2393 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2409 const size_t size = data.size();
2412 while (i<size && data[i]==
' ') i++;
2413 if (i<size && data[i]==
'|' && data[i]!=
'\n') i++,n++;
2418 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2421 if (j>0 && i>0) i--;
2422 while (i>0 && data[i]==
' ') i--;
2423 if (i>0 && data[i-1]!=
'\\' && data[i]==
'|') i--,n++;
2433 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2434 if (columns==1) columns++;
2438 if (n==2 && columns==0)
2450 size_t cc0=0, start=0,
end=0;
2454 if (i>=data.size() || cc0<1)
2466 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2475 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2490 const size_t size = data.size();
2492 size_t columns=0, start=0,
end=0;
2494 size_t headerStart = start;
2495 size_t headerEnd =
end;
2501 std::vector<int> columnAlignment(columns);
2503 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2509 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2510 if (data[j]==
'-') startFound=
TRUE;
2513 if (data[j]==
'-') rightMarker=
FALSE;
2514 else if (data[j]==
':') rightMarker=
TRUE;
2515 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2539 std::vector<std::vector<TableCell> > tableContents;
2541 size_t m = headerStart;
2542 std::vector<TableCell> headerContents(columns);
2543 for (k=0;k<columns;k++)
2545 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2547 headerContents[k].cellText += data[m++];
2552 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2553 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2555 tableContents.push_back(headerContents);
2561 if (cc!=columns)
break;
2565 std::vector<TableCell> rowContents(columns);
2568 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2572 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2573 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2578 rowContents[k].cellText += data[j];
2584 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2585 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2586 tableContents.push_back(rowContents);
2592 out+=
"<table class=\"markdownTable\">";
2593 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2594 for (
size_t row = 0; row < tableContents.size(); row++)
2600 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2604 out+=
"\n<tr class=\"markdownTableRowEven\">";
2609 out+=
"\n <tr class=\"markdownTableHead\">";
2611 for (
size_t c = 0; c < columns; c++)
2614 QCString cellText(tableContents[row][c].cellText);
2620 if (tableContents[row][c].cellText ==
"^")
2624 if (tableContents[row][c].colSpan)
2626 int cr =
static_cast<int>(c);
2627 while ( cr >= 0 && tableContents[row][cr].colSpan)
2631 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2633 size_t rowSpan = 1, spanRow = row+1;
2634 while ((spanRow < tableContents.size()) &&
2635 (tableContents[spanRow][c].cellText ==
"^"))
2641 out+=
" <" + cellTag +
" " + cellClass;
2643 switch (columnAlignment[c])
2655 out+=
" rowspan=\"" + spanStr +
"\"";
2661 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2670 out+=
" colspan=\"" + spanStr +
"\"";
2674 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2677 cellClass =
"class=\"markdownTableBody";
2693 while (i<data.size() && data[i]!=
'\n')
2695 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2698 if (i>=data.size()) {
return 0; }
2699 if (i<2) {
return 0; }
2700 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2740 out+=
"</"+hTag+
">\n";
2743 else if (data.size()>0)
2745 size_t tmpSize = data.size();
2746 if (data[data.size()-1] ==
'\n') tmpSize--;
2747 out+=data.substr(0,tmpSize);
2751 out+=
"\\ilinebr<br>";
2753 if (tmpSize != data.size())
out+=
'\n';
2759 {
"[!note]",
"\\note" },
2760 {
"[!warning]",
"\\warning" },
2761 {
"[!tip]",
"\\remark" },
2762 {
"[!caution]",
"\\attention" },
2763 {
"[!important]",
"\\important" }
2772 const size_t size = data.size();
2773 std::string startCmd;
2774 int isGitHubAlert =
false;
2775 int isGitHubFirst =
false;
2780 while (
end<=size && data[
end-1]!=
'\n')
end++;
2785 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2787 if (data[j]==
'>') { level++; indent=j+1; }
2788 else if (j>0 && data[j-1]==
'>') indent=j+1;
2791 if (indent>0 && j>0 && data[j-1]==
'>' &&
2792 !(j==size || data[j]==
'\n'))
2809 isGitHubAlert =
true;
2810 isGitHubFirst =
true;
2811 startCmd = it->second;
2816 if (level!=1 || !isGitHubAlert)
2818 for (
int l=curLevel;l<level-1;l++)
2820 out+=
"<blockquote>";
2822 out +=
"<blockquote>‍";
2824 else if (!startCmd.empty())
2826 out += startCmd +
" ";
2829 else if (level<curLevel)
2832 if (level==0 && isGitHubAlert)
2838 out +=
"</blockquote>\\ilinebr ";
2847 if (curLevel!=0 || !isGitHubAlert)
2849 std::string_view txt = data.substr(indent,
end-indent);
2852 if (!isGitHubFirst)
out +=
"<br>";
2859 isGitHubFirst =
false;
2874 for (
int l=0;l<curLevel;l++)
2876 out+=
"</blockquote>";
2887 size_t size = data.size();
2888 while (i<data.size() && data[i]==
' ') i++;
2889 if (i<size+8 && data[i]==
'\\' &&
qstrncmp(&data[i+1],
"ifile \"",7)==0)
2891 size_t locStart = i;
2892 if (i>offset) locStart--;
2895 while (i+9<size && data[i]!=
'\n')
2897 if (data[i]==
'\\' &&
qstrncmp(&data[i+1],
"ilinebr ",8)==0)
2907 location=data.substr(locStart,i-locStart);
2909 while (indent>0 && i<size && data[i]==
' ') i++,indent--;
2910 if (i<size && data[i]==
'\n') i++;
2921 const size_t size = data.size();
2924 out+=
"@iverbatim\n";
2926 std::string location;
2931 while (
end<=size && data[
end-1]!=
'\n')
end++;
2934 while (j<
end && data[j]==
' ') j++,indent++;
2944 while (emptyLines>0)
2952 std::string lineLoc;
2957 out+=data.substr(offset,
end-offset);
2965 out+=
"@endiverbatim";
2966 if (!location.empty())
2974 while (emptyLines>0)
2990 const size_t size = data.size();
2991 size_t nb=0,
end=offset+1, j=0;
2996 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
2997 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3004 size_t l = endBlockName.
length();
3005 for (;
end+l+1<size;
end++)
3007 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3008 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3022 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3023 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3026 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3027 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3038 else if (nb==0 && data[
end-1]==
'`')
3040 while (
end<=size && data[
end-1]==
'`')
end++,nb++;
3042 else if (nb>0 && data[
end-1]==
'`')
3045 while (
end<=size && data[
end-1]==
'`')
end++,enb++;
3059 size_t blockStart,
size_t blockEnd)
3062 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3063 const size_t size=data.size();
3065 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3086 size_t pi=std::string::npos;
3087 bool newBlock =
false;
3088 bool insideList =
false;
3089 size_t currentIndent = refIndent;
3090 size_t listIndent = refIndent;
3091 const size_t size = data.size();
3098 size_t lineIndent=0;
3099 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3105 if (insideList && lineIndent<currentIndent)
3108 currentIndent = refIndent;
3116 if (listIndent<currentIndent+4)
3120 currentIndent = listIndent;
3127 currentIndent = listIndent;
3136 if (pi!=std::string::npos)
3138 size_t blockStart=0, blockEnd=0, blockOffset=0;
3139 if (
isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3141 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3143 size_t cmdPos = pi+blockStart+1;
3144 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3150 if (pl[ii]==
'\n') nl++;
3153 bool addNewLines =
false;
3155 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3164 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3165 addNewLines =
false;
3180 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3182 if (addNewLines)
out+=
'\n';
3187 addSpecialCommand(
"startuml",
"enduml");
3191 addSpecialCommand(
"dot",
"enddot");
3193 else if (lang==
"msc")
3195 addSpecialCommand(
"msc",
"endmsc");
3202 pi=std::string::npos;
3206 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3209 pi=std::string::npos;
3216 out+=data.substr(pi,i-pi);
3222 if (pi!=std::string::npos && pi<size)
3230 out+=data.substr(pi);
3244 size_t pi = std::string::npos;
3260 size_t currentIndent = indent;
3261 size_t listIndent = indent;
3262 bool insideList =
false;
3263 bool newBlock =
false;
3266 while (i<data.size())
3271 size_t lineIndent=0;
3273 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3279 if (insideList && lineIndent<currentIndent)
3282 currentIndent = indent;
3290 if (listIndent<currentIndent+4)
3294 currentIndent = listIndent;
3301 currentIndent = listIndent;
3313 if (pi!=std::string::npos)
3315 size_t blockStart=0, blockEnd=0, blockOffset=0;
3317 size_t blockIndent = currentIndent;
3321 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3325 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3335 size_t l = endBlockName.
length();
3336 while (i+l<data.size())
3338 if ((data[i]==
'\\' || data[i]==
'@') &&
3339 data[i-1]!=
'\\' && data[i-1]!=
'@')
3356 while (pi<data.size() && data[pi]==
' ') pi++;
3357 QCString header = data.substr(pi,i-pi-1);
3364 out+=level==1?
"@section ":
"@subsection ";
3372 out+=level==1?
"<h1>":
"<h2>";
3374 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3381 pi=std::string::npos;
3386 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3394 else if (
isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3400 pi=std::string::npos;
3408 pi=std::string::npos;
3415 pi=std::string::npos;
3428 if (pi!=std::string::npos && pi<data.size())
3430 if (
isLinkRef(data.substr(pi),
id,link,title))
3450 std::string_view data(docs.
str());
3451 const size_t size = data.size();
3454 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3458 if (i+5<size && data[i]==
'<' &&
qstrncmp(&data[i],
"<!--!",5)==0)
3461 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3467 (data[i]==
'\\' || data[i]==
'@') &&
3468 (
qstrncmp(&data[i+1],
"page ",5)==0 ||
qstrncmp(&data[i+1],
"mainpage",8)==0)
3471 if (
qstrncmp(&data[i+1],
"page ",5)==0)
3482 else if (i+1<size &&
3483 (data[i]==
'\\' || data[i]==
'@') &&
3503 std::string_view data(docs_org.
str());
3504 const size_t size = data.size();
3506 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3508 if (data[i]==
'\n') prepend++;
3511 if (i>=size) {
return QCString(); }
3513 while (end1<size && data[end1-1]!=
'\n') end1++;
3520 while (end2<size && data[end2-1]!=
'\n') end2++;
3521 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3523 title = data.substr(i,end1-i-1);
3524 docs+=
"\n\n"+docs_org.
mid(end2);
3525 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3531 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3534 docs+=docs_org.
mid(end1);
3539 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3550 if (input.
isEmpty())
return input;
3555 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3556 s =
detab(s,refIndent);
3560 s =
prv->processQuotations(s.
view(),refIndent);
3564 s =
prv->processBlocks(s.
view(),refIndent);
3582 const char *p = result.
data();
3585 while (*p==
' ') p++;
3586 while (*p==
'\n') {startNewlines++;p++;};
3589 if (p>result.
data())
3592 result = result.
mid(
static_cast<int>(p-result.
data()));
3605 if (i!=-1) baseFn = baseFn.
left(i);
3629 const char *fileBuf,
3630 const std::shared_ptr<Entry> &root,
3633 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3636 current->fileName = fileName;
3637 current->docFile = fileName;
3638 current->docLine = 1;
3643 bool isIdGenerated =
false;
3651 int indentLevel=title.
isEmpty() ? 0 : -1;
3658 bool wasEmpty =
id.isEmpty();
3659 if (wasEmpty)
id = mdFileNameId;
3665 if (!mdfileAsMainPage.
isEmpty() &&
3669 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3670 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3672 else if (
id==
"mainpage" ||
id==
"index")
3674 if (title.
isEmpty()) title = titleFn;
3675 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3676 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3678 else if (isSubdirDocs)
3680 docs.
prepend(
"@dir\\ilinebr ");
3691 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3693 else if (!generatedId.
isEmpty())
3695 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3697 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3700 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3702 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3704 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3709 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3711 std::string s = docs.str();
3714 QCString orgLabel = match[1].str();
3715 QCString orgTitle = match[2].str();
3718 docs = docs.
left(match[1].position())+
3721 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3733 p->commentScanner.enterFile(fileName,lineNr);
3735 bool needsEntry =
false;
3739 while (
p->commentScanner.parseCommentBlock(
3757 QCString docFile = current->docFile;
3758 root->moveToSubEntryAndRefresh(current);
3760 current->docFile = docFile;
3761 current->docLine = lineNr;
3766 root->moveToSubEntryAndKeep(current);
3768 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.
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 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.