Doxygen
Loading...
Searching...
No Matches
Markdown::Private Struct Reference
Collaboration diagram for Markdown::Private:

Classes

struct  LinkRef

Public Member Functions

 Private (const QCString &fn, int line, int indent)
QCString processQuotations (std::string_view data, size_t refIndent)
QCString processBlocks (std::string_view data, size_t indent)
QCString isBlockCommand (std::string_view data, size_t offset)
size_t isSpecialCommand (std::string_view data, size_t offset)
size_t findEndOfLine (std::string_view data, size_t offset)
int processHtmlTagWrite (std::string_view data, size_t offset, bool doWrite)
 Process a HTML tag.
int processHtmlTag (std::string_view data, size_t offset)
int processEmphasis (std::string_view data, size_t offset)
int processEmphasis1 (std::string_view data, char c)
 process single emphasis
int processEmphasis2 (std::string_view data, char c)
 process double emphasis
int processEmphasis3 (std::string_view data, char c)
 Parsing triple emphasis.
int processNmdash (std::string_view data, size_t offset)
 Process ndash and mdashes.
int processQuoted (std::string_view data, size_t offset)
 Process quoted section "...", can contain one embedded newline.
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)
int processLink (std::string_view data, size_t offset)
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, or we are at the end of a paragraph.
void addStrEscapeUtf8Nbsp (std::string_view data)
void processInline (std::string_view data)
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)
int isHeaderline (std::string_view data, bool allowAdjustLevel)
 returns whether the line is a setext-style hdr underline
int isAtxHeader (std::string_view data, QCString &header, QCString &id, bool allowAdjustLevel, bool *pIsIdGenerated=nullptr)
void writeOneLineHeaderOrRuler (std::string_view data)
void writeFencedCodeBlock (std::string_view data, std::string_view lang, size_t blockStart, size_t blockEnd)
size_t writeBlockQuote (std::string_view data)
size_t writeCodeBlock (std::string_view, size_t refIndent)
size_t writeTableBlock (std::string_view data)
QCString extractTitleId (QCString &title, int level, bool *pIsIdGenerated=nullptr)

Public Attributes

std::unordered_map< std::string, LinkReflinkRefs
QCString fileName
int lineNr = 0
int indentLevel =0
QCString out

Detailed Description

Definition at line 130 of file markdown.cpp.

Constructor & Destructor Documentation

◆ Private()

Markdown::Private::Private ( const QCString & fn,
int line,
int indent )
inline

Definition at line 132 of file markdown.cpp.

132: fileName(fn), lineNr(line), indentLevel(indent) { }

References fileName, indentLevel, and lineNr.

Member Function Documentation

◆ addStrEscapeUtf8Nbsp()

void Markdown::Private::addStrEscapeUtf8Nbsp ( std::string_view data)

Definition at line 1789 of file markdown.cpp.

1790{
1791 AUTO_TRACE("{}",Trace::trunc(data));
1792 if (Portable::strnstr(data.data(),g_doxy_nbsp,data.size())==nullptr) // no escape needed -> fast
1793 {
1794 out+=data;
1795 }
1796 else // escape needed -> slow
1797 {
1798 out+=substitute(QCString(data),g_doxy_nbsp,g_utf8_nbsp);
1799 }
1800}
#define AUTO_TRACE(...)
Definition docnode.cpp:46
static const char * g_doxy_nbsp
Definition markdown.cpp:218
static const char * g_utf8_nbsp
Definition markdown.cpp:217
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
Definition portable.cpp:601
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571

References AUTO_TRACE, g_doxy_nbsp, g_utf8_nbsp, out, Portable::strnstr(), substitute(), and Trace::trunc().

Referenced by processSpecialCommand(), and writeFencedCodeBlock().

◆ extractTitleId()

QCString Markdown::Private::extractTitleId ( QCString & title,
int level,
bool * pIsIdGenerated = nullptr )

Definition at line 2090 of file markdown.cpp.

2091{
2092 AUTO_TRACE("title={} level={}",Trace::trunc(title),level);
2093 // match e.g. '{#id-b11} ' and capture 'id-b11'
2094 static const reg::Ex r2(R"({#(\a[\w-]*)}\s*$)");
2095 reg::Match match;
2096 std::string ti = title.str();
2097 if (reg::search(ti,match,r2))
2098 {
2099 std::string id = match[1].str();
2100 title = title.left(match.position());
2101 if (AnchorGenerator::instance().reserve(id)>0)
2102 {
2103 warn(fileName, lineNr, "An automatically generated id already has the name '{}'!", id);
2104 }
2105 //printf("found match id='%s' title=%s\n",qPrint(id),qPrint(title));
2106 AUTO_TRACE_EXIT("id={}",id);
2107 return id;
2108 }
2109 if (((level>0) && (level<=Config_getInt(TOC_INCLUDE_HEADINGS))) || (Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2110 {
2111 QCString id = AnchorGenerator::instance().generate(ti);
2112 if (pIsIdGenerated) *pIsIdGenerated=true;
2113 //printf("auto-generated id='%s' title='%s'\n",qPrint(id),qPrint(title));
2114 AUTO_TRACE_EXIT("id={}",id);
2115 return id;
2116 }
2117 //printf("no id found in title '%s'\n",qPrint(title));
2118 return "";
2119}
static AnchorGenerator & instance()
Returns the singleton instance.
Definition anchor.cpp:38
std::string generate(const std::string &title)
generates an anchor for a section with title.
Definition anchor.cpp:53
const std::string & str() const
Definition qcstring.h:552
QCString left(size_t len) const
Definition qcstring.h:229
#define Config_getInt(name)
Definition config.h:34
#define Config_getEnum(name)
Definition config.h:35
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:48
#define warn(file, line, fmt,...)
Definition message.h:97
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.
Definition regex.cpp:844
bool match(std::string_view str, Match &match, const Ex &re)
Matches a given string str for a match against regular expression re.
Definition regex.cpp:855

References AUTO_TRACE, AUTO_TRACE_EXIT, Config_getEnum, Config_getInt, fileName, AnchorGenerator::generate(), AnchorGenerator::instance(), QCString::left(), lineNr, reg::search(), QCString::str(), Trace::trunc(), and warn.

Referenced by isAtxHeader(), and processBlocks().

◆ findEmphasisChar()

size_t Markdown::Private::findEmphasisChar ( std::string_view data,
char c,
size_t c_size )

looks for the next emph char, skipping other constructs, and stopping when either it is found, or we are at the end of a paragraph.

Definition at line 730 of file markdown.cpp.

731{
732 AUTO_TRACE("data='{}' c={} c_size={}",Trace::trunc(data),c,c_size);
733 size_t i = 1;
734 const size_t size = data.size();
735
736 while (i<size)
737 {
738 while (i<size && data[i]!=c &&
739 data[i]!='\\' && data[i]!='@' &&
740 !(data[i]=='/' && data[i-1]=='<') && // html end tag also ends emphasis
741 data[i]!='\n') i++;
742 // avoid overflow (unclosed emph token)
743 if (i==size)
744 {
745 return 0;
746 }
747 //printf("findEmphasisChar: data=[%s] i=%d c=%c\n",data,i,data[i]);
748
749 // not counting escaped chars or characters that are unlikely
750 // to appear as the end of the emphasis char
751 if (ignoreCloseEmphChar(data[i-1],data[i]))
752 {
753 i++;
754 continue;
755 }
756 else
757 {
758 // get length of emphasis token
759 size_t len = 0;
760 while (i+len<size && data[i+len]==c)
761 {
762 len++;
763 }
764
765 if (len>0)
766 {
767 if (len!=c_size || (i+len<size && isIdChar(data[i+len]))) // to prevent touching some_underscore_identifier
768 {
769 i+=len;
770 continue;
771 }
772 AUTO_TRACE_EXIT("result={}",i);
773 return static_cast<int>(i); // found it
774 }
775 }
776
777 // skipping a code span
778 if (data[i]=='`')
779 {
780 int snb=0;
781 while (i < size && data[i] == '`')
782 {
783 snb++;
784 i++;
785 }
786
787 // find same pattern to end the span
788 int enb=0;
789 while (i<size && enb<snb)
790 {
791 if (data[i]=='`') enb++;
792 if (snb==1 && data[i]=='\'') break; // ` ended by '
793 i++;
794 }
795 }
796 else if (data[i]=='@' || data[i]=='\\')
797 { // skip over blocks that should not be processed
798 QCString endBlockName = isBlockCommand(data.substr(i),i);
799 if (!endBlockName.isEmpty())
800 {
801 i++;
802 size_t l = endBlockName.length();
803 while (i+l<size)
804 {
805 if ((data[i]=='\\' || data[i]=='@') && // command
806 data[i-1]!='\\' && data[i-1]!='@') // not escaped
807 {
808 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
809 {
810 break;
811 }
812 }
813 i++;
814 }
815 }
816 else if (i+1<size && isIdChar(data[i+1])) // @cmd, stop processing, see bug 690385
817 {
818 return 0;
819 }
820 else
821 {
822 i++;
823 }
824 }
825 else if (data[i-1]=='<' && data[i]=='/') // html end tag invalidates emphasis
826 {
827 return 0;
828 }
829 else if (data[i]=='\n') // end * or _ at paragraph boundary
830 {
831 i++;
832 while (i<size && data[i]==' ') i++;
833 if (i>=size || data[i]=='\n')
834 {
835 return 0;
836 } // empty line -> paragraph
837 }
838 else // should not get here!
839 {
840 i++;
841 }
842 }
843 return 0;
844}
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
static constexpr bool ignoreCloseEmphChar(char c, char cn)
Definition markdown.cpp:116
static constexpr bool isIdChar(char c)
Definition markdown.cpp:77
int qstrncmp(const char *str1, const char *str2, size_t len)
Definition qcstring.h:75
QCString isBlockCommand(std::string_view data, size_t offset)
Definition markdown.cpp:388

References AUTO_TRACE, AUTO_TRACE_EXIT, QCString::data(), ignoreCloseEmphChar(), isBlockCommand(), QCString::isEmpty(), isIdChar(), QCString::length(), qstrncmp(), and Trace::trunc().

Referenced by processEmphasis1(), processEmphasis2(), and processEmphasis3().

◆ findEndOfLine()

size_t Markdown::Private::findEndOfLine ( std::string_view data,
size_t offset )

Definition at line 3119 of file markdown.cpp.

3120{
3121 AUTO_TRACE("data='{}'",Trace::trunc(data));
3122 // find end of the line
3123 const size_t size = data.size();
3124 size_t nb=0, end=offset+1, j=0;
3125 while (end<=size && (j=isNewline(data.substr(end-1)))==0)
3126 {
3127 // while looking for the end of the line we might encounter a block
3128 // that needs to be passed unprocessed.
3129 if ((data[end-1]=='\\' || data[end-1]=='@') && // command
3130 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@')) // not escaped
3131 )
3132 {
3133 QCString endBlockName = isBlockCommand(data.substr(end-1),end-1);
3134 end++;
3135 if (!endBlockName.isEmpty())
3136 {
3137 size_t l = endBlockName.length();
3138 for (;end+l+1<size;end++) // search for end of block marker
3139 {
3140 if ((data[end]=='\\' || data[end]=='@') &&
3141 data[end-1]!='\\' && data[end-1]!='@'
3142 )
3143 {
3144 if (qstrncmp(&data[end+1],endBlockName.data(),l)==0)
3145 {
3146 // found end marker, skip over this block
3147 //printf("feol.block out={%s}\n",qPrint(QCString(data+i).left(end+l+1-i)));
3148 end = end + l + 2;
3149 break;
3150 }
3151 }
3152 }
3153 }
3154 }
3155 else if (nb==0 && data[end-1]=='<' && size>=6 && end+6<size &&
3156 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@'))
3157 )
3158 {
3159 if (tolower(data[end])=='p' && tolower(data[end+1])=='r' &&
3160 tolower(data[end+2])=='e' && (data[end+3]=='>' || data[end+3]==' ')) // <pre> tag
3161 {
3162 // skip part until including </pre>
3163 end = end + processHtmlTagWrite(data.substr(end-1),end-1,false);
3164 break;
3165 }
3166 else
3167 {
3168 end++;
3169 }
3170 }
3171 else if (nb==0 && data[end-1]=='`')
3172 {
3173 while (end <= size && data[end - 1] == '`')
3174 {
3175 end++;
3176 nb++;
3177 }
3178 }
3179 else if (nb>0 && data[end-1]=='`')
3180 {
3181 size_t enb=0;
3182 while (end <= size && data[end - 1] == '`')
3183 {
3184 end++;
3185 enb++;
3186 }
3187 if (enb==nb) nb=0;
3188 }
3189 else
3190 {
3191 end++;
3192 }
3193 }
3194 if (j>0) end+=j-1;
3195 AUTO_TRACE_EXIT("offset={} end={}",offset,end);
3196 return end;
3197}
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
size_t isNewline(std::string_view data)
Definition markdown.cpp:225
int processHtmlTagWrite(std::string_view data, size_t offset, bool doWrite)
Process a HTML tag.

References AUTO_TRACE, AUTO_TRACE_EXIT, QCString::data(), end(), isBlockCommand(), QCString::isEmpty(), isNewline(), QCString::length(), processHtmlTagWrite(), qstrncmp(), and Trace::trunc().

Referenced by processBlocks(), and processQuotations().

◆ isAtxHeader()

int Markdown::Private::isAtxHeader ( std::string_view data,
QCString & header,
QCString & id,
bool allowAdjustLevel,
bool * pIsIdGenerated = nullptr )

Definition at line 2122 of file markdown.cpp.

2124{
2125 AUTO_TRACE("data='{}' header={} id={} allowAdjustLevel={}",Trace::trunc(data),Trace::trunc(header),id,allowAdjustLevel);
2126 size_t i = 0;
2127 int level = 0, blanks=0;
2128 const size_t size = data.size();
2129
2130 // find start of header text and determine heading level
2131 while (i<size && data[i]==' ') i++;
2132 if (i>=size || data[i]!='#')
2133 {
2134 return 0;
2135 }
2136 while (i < size && data[i] == '#')
2137 {
2138 i++;
2139 level++;
2140 }
2141 if (level>SectionType::MaxLevel) // too many #'s -> no section
2142 {
2143 return 0;
2144 }
2145 while (i < size && data[i] == ' ')
2146 {
2147 i++;
2148 blanks++;
2149 }
2150 if (level==1 && blanks==0)
2151 {
2152 return 0; // special case to prevent #someid seen as a header (see bug 671395)
2153 }
2154
2155 // find end of header text
2156 size_t end=i;
2157 while (end<size && data[end]!='\n') end++;
2158 while (end>i && (data[end-1]=='#' || data[end-1]==' ')) end--;
2159
2160 // store result
2161 header = data.substr(i,end-i);
2162 id = extractTitleId(header, level, pIsIdGenerated);
2163 if (!id.isEmpty()) // strip #'s between title and id
2164 {
2165 int idx=static_cast<int>(header.length())-1;
2166 while (idx>=0 && (header.at(idx)=='#' || header.at(idx)==' ')) idx--;
2167 header=header.left(idx+1);
2168 }
2169
2170 if (allowAdjustLevel && level==1 && indentLevel==-1)
2171 {
2172 // in case we find a `# Section` on a markdown page that started with the same level
2173 // header, we no longer need to artificially decrease the paragraph level.
2174 // So both
2175 // -------------------
2176 // # heading 1 <-- here we set g_indentLevel to -1
2177 // # heading 2 <-- here we set g_indentLevel back to 0 such that this will be a @section
2178 // -------------------
2179 // and
2180 // -------------------
2181 // # heading 1 <-- here we set g_indentLevel to -1
2182 // ## heading 2 <-- here we keep g_indentLevel at -1 such that @subsection will be @section
2183 // -------------------
2184 // will convert to
2185 // -------------------
2186 // @page md_page Heading 1
2187 // @section autotoc_md1 Heading 2
2188 // -------------------
2189
2190 indentLevel=0;
2191 }
2192 int res = level+indentLevel;
2193 AUTO_TRACE_EXIT("result={}",res);
2194 return res;
2195}
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
static constexpr int MaxLevel
Definition section.h:39
QCString extractTitleId(QCString &title, int level, bool *pIsIdGenerated=nullptr)

References QCString::at(), AUTO_TRACE, AUTO_TRACE_EXIT, end(), extractTitleId(), indentLevel, QCString::left(), QCString::length(), SectionType::MaxLevel, and Trace::trunc().

Referenced by writeOneLineHeaderOrRuler().

◆ isBlockCommand()

QCString Markdown::Private::isBlockCommand ( std::string_view data,
size_t offset )

Definition at line 388 of file markdown.cpp.

389{
390 QCString result;
391 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
392
393 using EndBlockFunc = QCString (*)(const std::string &,bool,char);
394
395 static constexpr auto getEndBlock = [](const std::string &blockName,bool,char) -> QCString
396 {
397 return "end"+blockName;
398 };
399 static constexpr auto getEndCode = [](const std::string &blockName,bool openBracket,char) -> QCString
400 {
401 return openBracket ? QCString("}") : "end"+blockName;
402 };
403 static constexpr auto getEndUml = [](const std::string &/* blockName */,bool,char) -> QCString
404 {
405 return "enduml";
406 };
407 static constexpr auto getEndFormula = [](const std::string &/* blockName */,bool,char nextChar) -> QCString
408 {
409 switch (nextChar)
410 {
411 case '$': return "f$";
412 case '(': return "f)";
413 case '[': return "f]";
414 case '{': return "f}";
415 }
416 return "";
417 };
418
419 // table mapping a block start command to a function that can return the matching end block string
420 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
421 {
422 { "dot", getEndBlock },
423 { "code", getEndCode },
424 { "icode", getEndBlock },
425 { "msc", getEndBlock },
426 { "verbatim", getEndBlock },
427 { "iverbatim", getEndBlock },
428 { "iliteral", getEndBlock },
429 { "latexonly", getEndBlock },
430 { "htmlonly", getEndBlock },
431 { "xmlonly", getEndBlock },
432 { "rtfonly", getEndBlock },
433 { "manonly", getEndBlock },
434 { "docbookonly", getEndBlock },
435 { "startuml", getEndUml },
436 { "f", getEndFormula }
437 };
438
439 const size_t size = data.size();
440 bool openBracket = offset>0 && data.data()[-1]=='{';
441 bool isEscaped = offset>0 && (data.data()[-1]=='\\' || data.data()[-1]=='@');
442 if (isEscaped) return result;
443
444 size_t end=1;
445 while (end<size && (data[end]>='a' && data[end]<='z')) end++;
446 if (end==1) return result;
447 std::string blockName(data.substr(1,end-1));
448 auto it = blockNames.find(blockName);
449 if (it!=blockNames.end()) // there is a function assigned
450 {
451 result = it->second(blockName, openBracket, end<size ? data[end] : 0);
452 }
453 AUTO_TRACE_EXIT("result={}",result);
454 return result;
455}

References AUTO_TRACE, AUTO_TRACE_EXIT, end(), QCString::size(), and Trace::trunc().

Referenced by findEmphasisChar(), findEndOfLine(), processBlocks(), and processSpecialCommand().

◆ isHeaderline()

int Markdown::Private::isHeaderline ( std::string_view data,
bool allowAdjustLevel )

returns whether the line is a setext-style hdr underline

Definition at line 1895 of file markdown.cpp.

1896{
1897 AUTO_TRACE("data='{}' allowAdjustLevel",Trace::trunc(data),allowAdjustLevel);
1898 size_t i=0, c=0;
1899 const size_t size = data.size();
1900 while (i<size && data[i]==' ') i++;
1901 if (i==size) return 0;
1902
1903 // test of level 1 header
1904 if (data[i]=='=')
1905 {
1906 while (i < size && data[i] == '=')
1907 {
1908 i++;
1909 c++;
1910 }
1911 while (i<size && data[i]==' ') i++;
1912 int level = (c>1 && (i>=size || data[i]=='\n')) ? 1 : 0;
1913 if (allowAdjustLevel && level==1 && indentLevel==-1)
1914 {
1915 // In case a page starts with a header line we use it as title, promoting it to @page.
1916 // We set g_indentLevel to -1 to promoting the other sections if they have a deeper
1917 // nesting level than the page header, i.e. @section..@subsection becomes @page..@section.
1918 // In case a section at the same level is found (@section..@section) however we need
1919 // to undo this (and the result will be @page..@section).
1920 indentLevel=0;
1921 }
1922 AUTO_TRACE_EXIT("result={}",indentLevel+level);
1923 return indentLevel+level;
1924 }
1925 // test of level 2 header
1926 if (data[i]=='-')
1927 {
1928 while (i < size && data[i] == '-')
1929 {
1930 i++;
1931 c++;
1932 }
1933 while (i<size && data[i]==' ') i++;
1934 return (c>1 && (i>=size || data[i]=='\n')) ? indentLevel+2 : 0;
1935 }
1936 return 0;
1937}

References AUTO_TRACE, AUTO_TRACE_EXIT, indentLevel, and Trace::trunc().

Referenced by processBlocks().

◆ isSpecialCommand()

size_t Markdown::Private::isSpecialCommand ( std::string_view data,
size_t offset )

Definition at line 457 of file markdown.cpp.

458{
459 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
460
461 using EndCmdFunc = size_t (*)(std::string_view,size_t);
462
463 static constexpr auto endOfLine = [](std::string_view data_,size_t offset_) -> size_t
464 {
465 // skip until the end of line (allowing line continuation characters)
466 char lc = 0;
467 char c = 0;
468 while (offset_<data_.size() && ((c=data_[offset_])!='\n' || lc=='\\'))
469 {
470 if (c=='\\') lc='\\'; // last character was a line continuation
471 else if (c!=' ') lc=0; // rest line continuation
472 offset_++;
473 }
474 return offset_;
475 };
476
477 static constexpr auto endOfLabels = [](std::string_view data_,size_t offset_,bool multi_) -> size_t
478 {
479 if (offset_<data_.size() && data_[offset_]==' ') // we expect a space before the label
480 {
481 char c = 0;
482 offset_++;
483 bool done=false;
484 while (!done)
485 {
486 // skip over spaces
487 while (offset_<data_.size() && data_[offset_]==' ')
488 {
489 offset_++;
490 }
491 // skip over label
492 while (offset_<data_.size() && (c=data_[offset_])!=' ' && c!=',' && c!='\\' && c!='@' && c!='\n')
493 {
494 offset_++;
495 }
496 // optionally skip over a comma separated list of labels
497 if (multi_ && offset_<data_.size() && (data_[offset_]==',' || data_[offset_]==' '))
498 {
499 size_t off = offset_;
500 while (off<data_.size() && data_[off]==' ')
501 {
502 off++;
503 }
504 if (off<data_.size() && data_[off]==',')
505 {
506 offset_ = ++off;
507 }
508 else // no next label found
509 {
510 done=true;
511 }
512 }
513 else
514 {
515 done=true;
516 }
517 }
518 return offset_;
519 }
520 return 0;
521 };
522
523 static constexpr auto endOfLabel = [](std::string_view data_,size_t offset_) -> size_t
524 {
525 return endOfLabels(data_,offset_,false);
526 };
527
528 static constexpr auto endOfLabelOpt = [](std::string_view data_,size_t offset_) -> size_t
529 {
530 size_t index=offset_;
531 if (index<data_.size() && data_[index]==' ') // skip over optional spaces
532 {
533 index++;
534 while (index<data_.size() && data_[index]==' ') index++;
535 }
536 if (index<data_.size() && data_[index]=='{') // find matching '}'
537 {
538 index++;
539 char c = 0;
540 while (index<data_.size() && (c=data_[index])!='}' && c!='\\' && c!='@' && c!='\n') index++;
541 if (index==data_.size() || data_[index]!='}') return 0; // invalid option
542 offset_=index+1; // part after {...} is the option
543 }
544 return endOfLabel(data_,offset_);
545 };
546
547 static constexpr auto endOfParam = [](std::string_view data_,size_t offset_) -> size_t
548 {
549 size_t index=offset_;
550 if (index<data_.size() && data_[index]==' ') // skip over optional spaces
551 {
552 index++;
553 while (index<data_.size() && data_[index]==' ') index++;
554 }
555 if (index<data_.size() && data_[index]=='[') // find matching ']'
556 {
557 index++;
558 char c = 0;
559 while (index<data_.size() && (c=data_[index])!=']' && c!='\n') index++;
560 if (index==data_.size() || data_[index]!=']') return 0; // invalid parameter
561 offset_=index+1; // part after [...] is the parameter name
562 }
563 return endOfLabels(data_,offset_,true);
564 };
565
566 static constexpr auto endOfRetVal = [](std::string_view data_,size_t offset_) -> size_t
567 {
568 return endOfLabels(data_,offset_,true);
569 };
570
571 static constexpr auto endOfFuncLike = [](std::string_view data_,size_t offset_,bool allowSpaces) -> size_t
572 {
573 if (offset_<data_.size() && data_[offset_]==' ') // we expect a space before the name
574 {
575 char c=0;
576 offset_++;
577 // skip over spaces
578 while (offset_<data_.size() && data_[offset_]==' ')
579 {
580 offset_++;
581 }
582 // skip over name (and optionally type)
583 while (offset_<data_.size() && (c=data_[offset_])!='\n' && (allowSpaces || c!=' ') && c!='(')
584 {
585 if (literal_at(data_.substr(offset_),"\\ilinebr ")) break;
586 offset_++;
587 }
588 if (c=='(') // find the end of the function
589 {
590 int count=1;
591 offset_++;
592 while (offset_<data_.size() && (c=data_[offset_++]))
593 {
594 if (c=='(') count++;
595 else if (c==')') count--;
596 if (count==0) return offset_;
597 }
598 }
599 return offset_;
600 }
601 return 0;
602 };
603
604 static constexpr auto endOfFunc = [](std::string_view data_,size_t offset_) -> size_t
605 {
606 return endOfFuncLike(data_,offset_,true);
607 };
608
609 static constexpr auto endOfGuard = [](std::string_view data_,size_t offset_) -> size_t
610 {
611 return endOfFuncLike(data_,offset_,false);
612 };
613
614 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
615 {
616 { "a", endOfLabel },
617 { "addindex", endOfLine },
618 { "addtogroup", endOfLabel },
619 { "anchor", endOfLabel },
620 { "b", endOfLabel },
621 { "c", endOfLabel },
622 { "category", endOfLine },
623 { "cite", endOfLabel },
624 { "class", endOfLine },
625 { "concept", endOfLine },
626 { "copybrief", endOfFunc },
627 { "copydetails", endOfFunc },
628 { "copydoc", endOfFunc },
629 { "def", endOfFunc },
630 { "defgroup", endOfLabel },
631 { "diafile", endOfLine },
632 { "dir", endOfLine },
633 { "dockbookinclude",endOfLine },
634 { "dontinclude", endOfLine },
635 { "dotfile", endOfLine },
636 { "e", endOfLabel },
637 { "elseif", endOfGuard },
638 { "em", endOfLabel },
639 { "emoji", endOfLabel },
640 { "enum", endOfLabel },
641 { "example", endOfLine },
642 { "exception", endOfLine },
643 { "extends", endOfLabel },
644 { "file", endOfLine },
645 { "fn", endOfFunc },
646 { "headerfile", endOfLine },
647 { "htmlinclude", endOfLine },
648 { "ianchor", endOfLabelOpt },
649 { "idlexcept", endOfLine },
650 { "if", endOfGuard },
651 { "ifnot", endOfGuard },
652 { "image", endOfLine },
653 { "implements", endOfLine },
654 { "include", endOfLine },
655 { "includedoc", endOfLine },
656 { "includelineno", endOfLine },
657 { "ingroup", endOfLabel },
658 { "interface", endOfLine },
659 { "latexinclude", endOfLine },
660 { "maninclude", endOfLine },
661 { "memberof", endOfLabel },
662 { "mscfile", endOfLine },
663 { "namespace", endOfLabel },
664 { "noop", endOfLine },
665 { "overload", endOfLine },
666 { "p", endOfLabel },
667 { "package", endOfLabel },
668 { "page", endOfLabel },
669 { "paragraph", endOfLabel },
670 { "param", endOfParam },
671 { "property", endOfLine },
672 { "protocol", endOfLine },
673 { "qualifier", endOfLine },
674 { "ref", endOfLabel },
675 { "refitem", endOfLine },
676 { "related", endOfLabel },
677 { "relatedalso", endOfLabel },
678 { "relates", endOfLabel },
679 { "relatesalso", endOfLabel },
680 { "retval", endOfRetVal},
681 { "rtfinclude", endOfLine },
682 { "section", endOfLabel },
683 { "skip", endOfLine },
684 { "skipline", endOfLine },
685 { "snippet", endOfLine },
686 { "snippetdoc", endOfLine },
687 { "snippetlineno", endOfLine },
688 { "struct", endOfLine },
689 { "subpage", endOfLabel },
690 { "subparagraph", endOfLabel },
691 { "subsubparagraph",endOfLabel },
692 { "subsection", endOfLabel },
693 { "subsubsection", endOfLabel },
694 { "throw", endOfLabel },
695 { "throws", endOfLabel },
696 { "tparam", endOfLabel },
697 { "typedef", endOfLine },
698 { "plantumlfile", endOfLine },
699 { "union", endOfLine },
700 { "until", endOfLine },
701 { "var", endOfLine },
702 { "verbinclude", endOfLine },
703 { "weakgroup", endOfLabel },
704 { "xmlinclude", endOfLine },
705 { "xrefitem", endOfLabel }
706 };
707
708 bool isEscaped = offset>0 && (data.data()[-1]=='\\' || data.data()[-1]=='@');
709 if (isEscaped) return 0;
710
711 const size_t size = data.size();
712 size_t end=1;
713 while (end<size && (data[end]>='a' && data[end]<='z')) end++;
714 if (end==1) return 0;
715 std::string cmdName(data.substr(1,end-1));
716 size_t result=0;
717 auto it = cmdNames.find(cmdName);
718 if (it!=cmdNames.end()) // command with parameters that should be ignored by markdown
719 {
720 // find the end of the parameters
721 result = it->second(data,end);
722 }
723 AUTO_TRACE_EXIT("result={}",result);
724 return result;
725}
bool literal_at(const char *data, const char(&str)[N])
returns TRUE iff data points to a substring that matches string literal str
Definition stringutil.h:98

References AUTO_TRACE, AUTO_TRACE_EXIT, end(), literal_at(), and Trace::trunc().

Referenced by processSpecialCommand().

◆ processBlocks()

QCString Markdown::Private::processBlocks ( std::string_view data,
size_t indent )

Definition at line 3385 of file markdown.cpp.

3386{
3387 AUTO_TRACE("data='{}' indent={}",Trace::trunc(data),indent);
3388 out.clear();
3389 size_t pi = std::string::npos;
3390 QCString id,link,title;
3391
3392#if 0 // commented out, since starting with a comment block is probably a usage error
3393 // see also http://stackoverflow.com/q/20478611/784672
3394
3395 // special case when the documentation starts with a code block
3396 // since the first line is skipped when looking for a code block later on.
3397 if (end>codeBlockIndent && isCodeBlock(data,0,end,blockIndent))
3398 {
3399 i=writeCodeBlock(out,data,size,blockIndent);
3400 end=i+1;
3401 pi=-1;
3402 }
3403#endif
3404
3405 size_t currentIndent = indent;
3406 size_t listIndent = indent;
3407 bool insideList = false;
3408 bool newBlock = false;
3409 // process each line
3410 size_t i=0;
3411 while (i<data.size())
3412 {
3413 size_t end = findEndOfLine(data,i);
3414 // line is now found at [i..end)
3415
3416 size_t lineIndent=0;
3417 int level = 0;
3418 while (lineIndent<end && data[i+lineIndent]==' ') lineIndent++;
3419 //printf("** lineIndent=%d line=(%s)\n",lineIndent,qPrint(QCString(data+i).left(end-i)));
3420
3421 if (newBlock)
3422 {
3423 //printf("** end of block\n");
3424 if (insideList && lineIndent<currentIndent) // end of list
3425 {
3426 //printf("** end of list\n");
3427 currentIndent = indent;
3428 insideList = false;
3429 }
3430 newBlock = false;
3431 }
3432
3433 if ((listIndent=isListMarker(data.substr(i,end-i)))) // see if we need to increase the indent level
3434 {
3435 if (listIndent<currentIndent+4)
3436 {
3437 //printf("** start of list\n");
3438 insideList = true;
3439 currentIndent = listIndent;
3440 }
3441 }
3442 else if (isEndOfList(data.substr(i,end-i)))
3443 {
3444 //printf("** end of list\n");
3445 insideList = false;
3446 currentIndent = listIndent;
3447 }
3448 else if (isEmptyLine(data.substr(i,end-i)))
3449 {
3450 //printf("** new block\n");
3451 newBlock = true;
3452 }
3453
3454 //printf("indent=%d listIndent=%d blockIndent=%d\n",indent,listIndent,blockIndent);
3455
3456 //printf("findEndOfLine: pi=%d i=%d end=%d\n",pi,i,end);
3457
3458 if (pi!=std::string::npos)
3459 {
3460 size_t blockStart=0, blockEnd=0, blockOffset=0;
3461 QCString lang;
3462 size_t blockIndent = currentIndent;
3463 size_t ref = 0;
3464 //printf("isHeaderLine(%s)=%d\n",QCString(data+i).left(size-i).data(),level);
3465 QCString endBlockName;
3466 if (data[i]=='@' || data[i]=='\\') endBlockName = isBlockCommand(data.substr(i),i);
3467 if (!endBlockName.isEmpty())
3468 {
3469 // handle previous line
3470 if (isLinkRef(data.substr(pi,i-pi),id,link,title))
3471 {
3472 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3473 }
3474 else
3475 {
3476 writeOneLineHeaderOrRuler(data.substr(pi,i-pi));
3477 }
3478 out+=data[i];
3479 i++;
3480 size_t l = endBlockName.length();
3481 while (i+l<data.size())
3482 {
3483 if ((data[i]=='\\' || data[i]=='@') && // command
3484 data[i-1]!='\\' && data[i-1]!='@') // not escaped
3485 {
3486 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
3487 {
3488 out+=data[i];
3489 out+=endBlockName;
3490 i+=l+1;
3491 break;
3492 }
3493 }
3494 out+=data[i];
3495 i++;
3496 }
3497 }
3498 else if ((level=isHeaderline(data.substr(i),TRUE))>0)
3499 {
3500 //printf("Found header at %d-%d\n",i,end);
3501 while (pi<data.size() && data[pi]==' ') pi++;
3502 QCString header = data.substr(pi,i-pi-1);
3503 id = extractTitleId(header, level);
3504 //printf("header='%s' is='%s'\n",qPrint(header),qPrint(id));
3505 if (!header.isEmpty())
3506 {
3507 if (!id.isEmpty())
3508 {
3509 out+=level==1?"@section ":"@subsection ";
3510 out+=id;
3511 out+=" ";
3512 out+=header;
3513 out+="\n\n";
3514 }
3515 else
3516 {
3517 out+=level==1?"<h1>":"<h2>";
3518 out+=header;
3519 out+=level==1?"\n</h1>\n":"\n</h2>\n";
3520 }
3521 }
3522 else
3523 {
3524 out+="\n<hr>\n";
3525 }
3526 pi=std::string::npos;
3527 i=end;
3528 end=i+1;
3529 continue;
3530 }
3531 else if ((ref=isLinkRef(data.substr(pi),id,link,title)))
3532 {
3533 //printf("found link ref: id='%s' link='%s' title='%s'\n",
3534 // qPrint(id),qPrint(link),qPrint(title));
3535 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3536 i=ref+pi;
3537 end=i+1;
3538 }
3539 else if (isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset,fileName,lineNr))
3540 {
3541 //printf("Found FencedCodeBlock lang='%s' start=%d end=%d code={%s}\n",
3542 // qPrint(lang),blockStart,blockEnd,QCString(data+pi+blockStart).left(blockEnd-blockStart).data());
3543 writeFencedCodeBlock(data.substr(pi),lang.view(),blockStart,blockEnd);
3544 i=pi+blockOffset;
3545 pi=std::string::npos;
3546 end=i+1;
3547 continue;
3548 }
3549 else if (isCodeBlock(data.substr(i,end-i),i,blockIndent))
3550 {
3551 // skip previous line (it is empty anyway)
3552 i+=writeCodeBlock(data.substr(i),blockIndent);
3553 pi=std::string::npos;
3554 end=i+1;
3555 continue;
3556 }
3557 else if (isTableBlock(data.substr(pi)))
3558 {
3559 i=pi+writeTableBlock(data.substr(pi));
3560 pi=std::string::npos;
3561 end=i+1;
3562 continue;
3563 }
3564 else
3565 {
3566 writeOneLineHeaderOrRuler(data.substr(pi,i-pi));
3567 }
3568 }
3569 pi=i;
3570 i=end;
3571 }
3572 //printf("last line %d size=%d\n",i,size);
3573 if (pi!=std::string::npos && pi<data.size()) // deal with the last line
3574 {
3575 if (isLinkRef(data.substr(pi),id,link,title))
3576 {
3577 //printf("found link ref: id='%s' link='%s' title='%s'\n",
3578 // qPrint(id),qPrint(link),qPrint(title));
3579 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3580 }
3581 else
3582 {
3583 writeOneLineHeaderOrRuler(data.substr(pi));
3584 }
3585 }
3586
3587 return out;
3588}
std::string_view view() const
Definition qcstring.h:174
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 bool isEndOfList(std::string_view data)
static bool isCodeBlock(std::string_view data, size_t offset, size_t &indent)
static bool isEmptyLine(std::string_view data)
static const size_t codeBlockIndent
Definition markdown.cpp:219
static bool isFencedCodeBlock(std::string_view data, size_t refIndent, QCString &lang, size_t &start, size_t &end, size_t &offset, QCString &fileName, int lineNr)
static size_t isListMarker(std::string_view data)
static bool isTableBlock(std::string_view data)
Returns TRUE iff data points to the start of a table block.
#define TRUE
Definition qcstring.h:37
size_t writeTableBlock(std::string_view data)
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
std::unordered_map< std::string, LinkRef > linkRefs
Definition markdown.cpp:175
size_t writeCodeBlock(std::string_view, size_t refIndent)
size_t findEndOfLine(std::string_view data, size_t offset)
void writeOneLineHeaderOrRuler(std::string_view data)

References AUTO_TRACE, codeBlockIndent, QCString::data(), end(), extractTitleId(), fileName, findEndOfLine(), isBlockCommand(), isCodeBlock(), QCString::isEmpty(), isEmptyLine(), isEndOfList(), isFencedCodeBlock(), isHeaderline(), isLinkRef(), isListMarker(), isTableBlock(), QCString::length(), lineNr, linkRefs, out, qstrncmp(), TRUE, Trace::trunc(), QCString::view(), writeCodeBlock(), writeFencedCodeBlock(), writeOneLineHeaderOrRuler(), and writeTableBlock().

◆ processCodeSpan()

int Markdown::Private::processCodeSpan ( std::string_view data,
size_t offset )

` parsing a code span (assuming codespan != 0)

Definition at line 1675 of file markdown.cpp.

1676{
1677 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1678 const size_t size = data.size();
1679
1680 /* counting the number of backticks in the delimiter */
1681 size_t nb=0, end=0;
1682 while (nb<size && data[nb]=='`')
1683 {
1684 nb++;
1685 }
1686
1687 /* finding the next delimiter with the same amount of backticks */
1688 size_t i = 0;
1689 char pc = '`';
1690 bool markdownStrict = Config_getBool(MARKDOWN_STRICT);
1691 for (end=nb; end<size; end++)
1692 {
1693 //AUTO_TRACE_ADD("c={} nb={} i={} size={}",data[end],nb,i,size);
1694 if (data[end]=='`')
1695 {
1696 i++;
1697 if (nb==1) // `...`
1698 {
1699 if (end+1<size && data[end+1]=='`') // skip over `` inside `...`
1700 {
1701 AUTO_TRACE_ADD("case1.1");
1702 // skip
1703 end++;
1704 i=0;
1705 }
1706 else // normal end of `...`
1707 {
1708 AUTO_TRACE_ADD("case1.2");
1709 break;
1710 }
1711 }
1712 else if (i==nb) // ``...``
1713 {
1714 if (end+1<size && data[end+1]=='`') // do greedy match
1715 {
1716 // skip this quote and use the next one to terminate the sequence, e.g. ``X`Y```
1717 i--;
1718 AUTO_TRACE_ADD("case2.1");
1719 }
1720 else // normal end of ``...``
1721 {
1722 AUTO_TRACE_ADD("case2.2");
1723 break;
1724 }
1725 }
1726 }
1727 else if (data[end]=='\n')
1728 {
1729 // consecutive newlines
1730 if (pc == '\n')
1731 {
1732 AUTO_TRACE_EXIT("new paragraph");
1733 return 0;
1734 }
1735 pc = '\n';
1736 i = 0;
1737 }
1738 else if (!markdownStrict && data[end]=='\'' && nb==1 && (end+1==size || (end+1<size && data[end+1]!='\'' && !isIdChar(data[end+1]))))
1739 { // look for quoted strings like 'some word', but skip strings like `it's cool`
1740 out+="&lsquo;";
1741 out+=data.substr(nb,end-nb);
1742 out+="&rsquo;";
1743 AUTO_TRACE_EXIT("quoted end={}",end+1);
1744 return static_cast<int>(end+1);
1745 }
1746 else if (!markdownStrict && data[end]=='\'' && nb==2 && end+1<size && data[end+1]=='\'')
1747 { // look for '' to match a ``
1748 out+="&ldquo;";
1749 out+=data.substr(nb,end-nb);
1750 out+="&rdquo;";
1751 AUTO_TRACE_EXIT("double quoted end={}",end+1);
1752 return static_cast<int>(end+2);
1753 }
1754 else
1755 {
1756 if (data[end]!=' ') pc = data[end];
1757 i=0;
1758 }
1759 }
1760 if (i < nb && end >= size)
1761 {
1762 AUTO_TRACE_EXIT("no matching delimiter nb={} i={}",nb,i);
1763 if (nb>=3) // found ``` that is not at the start of the line, keep it as-is.
1764 {
1765 out+=data.substr(0,nb);
1766 return static_cast<int>(nb);
1767 }
1768 return 0; // no matching delimiter
1769 }
1770 while (end<size && data[end]=='`') // do greedy match in case we have more end backticks.
1771 {
1772 end++;
1773 }
1774
1775 //printf("found code span '%s'\n",qPrint(QCString(data+f_begin).left(f_end-f_begin)));
1776
1777 /* real code span */
1778 if (nb+nb < end)
1779 {
1780 QCString codeFragment = data.substr(nb, end-nb-nb);
1781 out+="<tt>";
1782 out+=escapeSpecialChars(codeFragment);
1783 out+="</tt>";
1784 }
1785 AUTO_TRACE_EXIT("result={} nb={}",end,nb);
1786 return static_cast<int>(end);
1787}
#define Config_getBool(name)
Definition config.h:33
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:47
static QCString escapeSpecialChars(const QCString &s)
Definition markdown.cpp:256

References AUTO_TRACE, AUTO_TRACE_ADD, AUTO_TRACE_EXIT, Config_getBool, end(), escapeSpecialChars(), isIdChar(), out, and Trace::trunc().

Referenced by Markdown::fill_table().

◆ processEmphasis()

int Markdown::Private::processEmphasis ( std::string_view data,
size_t offset )

Definition at line 1143 of file markdown.cpp.

1144{
1145 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1146 const size_t size = data.size();
1147
1148 if (isAllowedEmphStr(data,offset) || // invalid char before * or _
1149 (size>1 && data[0]!=data[1] && !(isIdChar(data[1]) || extraChar(data[1]))) || // invalid char after * or _
1150 (size>2 && data[0]==data[1] && !(isIdChar(data[2]) || extraChar(data[2])))) // invalid char after ** or __
1151 {
1152 AUTO_TRACE_EXIT("invalid surrounding characters");
1153 return 0;
1154 }
1155
1156 char c = data[0];
1157 int ret = 0;
1158 if (size>2 && c!='~' && data[1]!=c) // _bla or *bla
1159 {
1160 // whitespace cannot follow an opening emphasis
1161 if (data[1]==' ' || data[1]=='\n' ||
1162 (ret = processEmphasis1(data.substr(1), c)) == 0)
1163 {
1164 return 0;
1165 }
1166 AUTO_TRACE_EXIT("result={}",ret+1);
1167 return ret+1;
1168 }
1169 if (size>3 && data[1]==c && data[2]!=c) // __bla or **bla
1170 {
1171 if (data[2]==' ' || data[2]=='\n' ||
1172 (ret = processEmphasis2(data.substr(2), c)) == 0)
1173 {
1174 return 0;
1175 }
1176 AUTO_TRACE_EXIT("result={}",ret+2);
1177 return ret+2;
1178 }
1179 if (size>4 && c!='~' && data[1]==c && data[2]==c && data[3]!=c) // ___bla or ***bla
1180 {
1181 if (data[3]==' ' || data[3]=='\n' ||
1182 (ret = processEmphasis3(data.substr(3), c)) == 0)
1183 {
1184 return 0;
1185 }
1186 AUTO_TRACE_EXIT("result={}",ret+3);
1187 return ret+3;
1188 }
1189 return 0;
1190}
static constexpr bool isAllowedEmphStr(const std::string_view &data, size_t offset)
Definition markdown.cpp:108
static constexpr bool extraChar(char c)
Definition markdown.cpp:86
int processEmphasis1(std::string_view data, char c)
process single emphasis
Definition markdown.cpp:847
int processEmphasis3(std::string_view data, char c)
Parsing triple emphasis.
Definition markdown.cpp:913
int processEmphasis2(std::string_view data, char c)
process double emphasis
Definition markdown.cpp:881

References AUTO_TRACE, AUTO_TRACE_EXIT, extraChar(), isAllowedEmphStr(), isIdChar(), processEmphasis1(), processEmphasis2(), processEmphasis3(), and Trace::trunc().

Referenced by Markdown::fill_table().

◆ processEmphasis1()

int Markdown::Private::processEmphasis1 ( std::string_view data,
char c )

process single emphasis

Definition at line 847 of file markdown.cpp.

848{
849 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
850 size_t i = 0;
851 const size_t size = data.size();
852
853 /* skipping one symbol if coming from emph3 */
854 if (size>1 && data[0]==c && data[1]==c) { i=1; }
855
856 while (i<size)
857 {
858 size_t len = findEmphasisChar(data.substr(i), c, 1);
859 if (len==0) { return 0; }
860 i+=len;
861 if (i>=size) { return 0; }
862
863 if (i+1<size && data[i+1]==c)
864 {
865 i++;
866 continue;
867 }
868 if (data[i]==c && data[i-1]!=' ' && data[i-1]!='\n')
869 {
870 out+="<em>";
871 processInline(data.substr(0,i));
872 out+="</em>";
873 AUTO_TRACE_EXIT("result={}",i+1);
874 return static_cast<int>(i+1);
875 }
876 }
877 return 0;
878}
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,...
Definition markdown.cpp:730
void processInline(std::string_view data)

References AUTO_TRACE, AUTO_TRACE_EXIT, findEmphasisChar(), out, processInline(), and Trace::trunc().

Referenced by processEmphasis(), and processEmphasis3().

◆ processEmphasis2()

int Markdown::Private::processEmphasis2 ( std::string_view data,
char c )

process double emphasis

Definition at line 881 of file markdown.cpp.

882{
883 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
884 size_t i = 0;
885 const size_t size = data.size();
886
887 while (i<size)
888 {
889 size_t len = findEmphasisChar(data.substr(i), c, 2);
890 if (len==0)
891 {
892 return 0;
893 }
894 i += len;
895 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=' ' && data[i-1]!='\n')
896 {
897 if (c == '~') out+="<strike>";
898 else out+="<strong>";
899 processInline(data.substr(0,i));
900 if (c == '~') out+="</strike>";
901 else out+="</strong>";
902 AUTO_TRACE_EXIT("result={}",i+2);
903 return static_cast<int>(i+2);
904 }
905 i++;
906 }
907 return 0;
908}

References AUTO_TRACE, AUTO_TRACE_EXIT, findEmphasisChar(), out, processInline(), and Trace::trunc().

Referenced by processEmphasis(), and processEmphasis3().

◆ processEmphasis3()

int Markdown::Private::processEmphasis3 ( std::string_view data,
char c )

Parsing triple emphasis.

Finds the first closing tag, and delegates to the other emph

Definition at line 913 of file markdown.cpp.

914{
915 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
916 size_t i = 0;
917 const size_t size = data.size();
918
919 while (i<size)
920 {
921 size_t len = findEmphasisChar(data.substr(i), c, 3);
922 if (len==0)
923 {
924 return 0;
925 }
926 i+=len;
927
928 /* skip whitespace preceded symbols */
929 if (data[i]!=c || data[i-1]==' ' || data[i-1]=='\n')
930 {
931 continue;
932 }
933
934 if (i+2<size && data[i+1]==c && data[i+2]==c)
935 {
936 out+="<em><strong>";
937 processInline(data.substr(0,i));
938 out+="</strong></em>";
939 AUTO_TRACE_EXIT("result={}",i+3);
940 return static_cast<int>(i+3);
941 }
942 else if (i+1<size && data[i+1]==c)
943 {
944 // double symbol found, handing over to emph1
945 len = processEmphasis1(std::string_view(data.data()-2, size+2), c);
946 if (len==0)
947 {
948 return 0;
949 }
950 else
951 {
952 AUTO_TRACE_EXIT("result={}",len-2);
953 return static_cast<int>(len - 2);
954 }
955 }
956 else
957 {
958 // single symbol found, handing over to emph2
959 len = processEmphasis2(std::string_view(data.data()-1, size+1), c);
960 if (len==0)
961 {
962 return 0;
963 }
964 else
965 {
966 AUTO_TRACE_EXIT("result={}",len-1);
967 return static_cast<int>(len - 1);
968 }
969 }
970 }
971 return 0;
972}

References AUTO_TRACE, AUTO_TRACE_EXIT, findEmphasisChar(), out, processEmphasis1(), processEmphasis2(), processInline(), and Trace::trunc().

Referenced by processEmphasis().

◆ processHtmlTag()

int Markdown::Private::processHtmlTag ( std::string_view data,
size_t offset )

Definition at line 1137 of file markdown.cpp.

1138{
1139 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1140 return processHtmlTagWrite(data,offset,true);
1141}

References AUTO_TRACE, processHtmlTagWrite(), and Trace::trunc().

Referenced by Markdown::fill_table().

◆ processHtmlTagWrite()

int Markdown::Private::processHtmlTagWrite ( std::string_view data,
size_t offset,
bool doWrite )

Process a HTML tag.

Note that

..

are treated specially, in the sense that all code inside is written unprocessed

Definition at line 1043 of file markdown.cpp.

1044{
1045 AUTO_TRACE("data='{}' offset={} doWrite={}",Trace::trunc(data),offset,doWrite);
1046 if (offset>0 && data.data()[-1]=='\\') { return 0; } // escaped <
1047
1048 const size_t size = data.size();
1049
1050 // find the end of the html tag
1051 size_t i=1;
1052 size_t l=0;
1053 // compute length of the tag name
1054 while (i < size && isIdChar(data[i]))
1055 {
1056 i++;
1057 l++;
1058 }
1059 QCString tagName(data.substr(1,i-1));
1060 if (tagName.lower()=="pre") // found <pre> tag
1061 {
1062 bool insideStr=FALSE;
1063 while (i+6<size)
1064 {
1065 char c=data[i];
1066 if (!insideStr && c=='<') // potential start of html tag
1067 {
1068 if (data[i+1]=='/' &&
1069 tolower(data[i+2])=='p' && tolower(data[i+3])=='r' &&
1070 tolower(data[i+4])=='e' && tolower(data[i+5])=='>')
1071 { // found </pre> tag, copy from start to end of tag
1072 if (doWrite) out+=data.substr(0,i+6);
1073 //printf("found <pre>..</pre> [%d..%d]\n",0,i+6);
1074 AUTO_TRACE_EXIT("result={}",i+6);
1075 return static_cast<int>(i+6);
1076 }
1077 }
1078 else if (insideStr && c=='"')
1079 {
1080 if (data[i-1]!='\\') insideStr=FALSE;
1081 }
1082 else if (c=='"')
1083 {
1084 insideStr=TRUE;
1085 }
1086 i++;
1087 }
1088 }
1089 else // some other html tag
1090 {
1091 if (l>0 && i<size)
1092 {
1093 if (data[i]=='/' && i+1<size && data[i+1]=='>') // <bla/>
1094 {
1095 //printf("Found htmlTag={%s}\n",qPrint(QCString(data).left(i+2)));
1096 if (doWrite) out+=data.substr(0,i+2);
1097 AUTO_TRACE_EXIT("result={}",i+2);
1098 return static_cast<int>(i+2);
1099 }
1100 else if (data[i]=='>') // <bla>
1101 {
1102 //printf("Found htmlTag={%s}\n",qPrint(QCString(data).left(i+1)));
1103 if (doWrite) out+=data.substr(0,i+1);
1104 AUTO_TRACE_EXIT("result={}",i+1);
1105 return static_cast<int>(i+1);
1106 }
1107 else if (data[i]==' ') // <bla attr=...
1108 {
1109 i++;
1110 bool insideAttr=FALSE;
1111 while (i<size)
1112 {
1113 if (!insideAttr && data[i]=='"')
1114 {
1115 insideAttr=TRUE;
1116 }
1117 else if (data[i]=='"' && data[i-1]!='\\')
1118 {
1119 insideAttr=FALSE;
1120 }
1121 else if (!insideAttr && data[i]=='>') // found end of tag
1122 {
1123 //printf("Found htmlTag={%s}\n",qPrint(QCString(data).left(i+1)));
1124 if (doWrite) out+=data.substr(0,i+1);
1125 AUTO_TRACE_EXIT("result={}",i+1);
1126 return static_cast<int>(i+1);
1127 }
1128 i++;
1129 }
1130 }
1131 }
1132 }
1133 AUTO_TRACE_EXIT("not a valid html tag");
1134 return 0;
1135}
#define FALSE
Definition qcstring.h:34

References AUTO_TRACE, AUTO_TRACE_EXIT, FALSE, isIdChar(), QCString::lower(), out, TRUE, and Trace::trunc().

Referenced by findEndOfLine(), and processHtmlTag().

◆ processInline()

void Markdown::Private::processInline ( std::string_view data)

Definition at line 1865 of file markdown.cpp.

1866{
1867 AUTO_TRACE("data='{}'",Trace::trunc(data));
1868 size_t i=0;
1869 size_t end=0;
1870 Action_t action;
1871 const size_t size = data.size();
1872 while (i<size)
1873 {
1874 // skip over characters that do not trigger a specific action
1875 while (end<size && ((action=Markdown::actions[static_cast<uint8_t>(data[end])])==nullptr)) end++;
1876 // and add them to the output
1877 out+=data.substr(i,end-i);
1878 if (end>=size) break;
1879 i=end;
1880 // do the action matching a special character at i
1881 int iend = action(*this,data.substr(i),i);
1882 if (iend<=0) // update end
1883 {
1884 end=i+1-iend;
1885 }
1886 else // skip until end
1887 {
1888 i+=iend;
1889 end=i;
1890 }
1891 }
1892}
static ActionTable_t actions
Definition markdown.h:47
std::function< int(Private &, std::string_view, size_t)> Action_t
Definition markdown.h:44

References Markdown::actions, AUTO_TRACE, end(), out, and Trace::trunc().

Referenced by processEmphasis1(), processEmphasis2(), processEmphasis3(), and processLink().

◆ processLink()

int Markdown::Private::processLink ( std::string_view data,
size_t offset )

Definition at line 1235 of file markdown.cpp.

1236{
1237 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1238 const size_t size = data.size();
1239 QCString content;
1240 QCString link;
1241 QCString title;
1242 bool isImageLink = FALSE;
1243 bool isImageInline = FALSE;
1244 bool isToc = FALSE;
1245 size_t i=1;
1246 if (data[0]=='!')
1247 {
1248 isImageLink = TRUE;
1249 if (size<2 || data[1]!='[')
1250 {
1251 return 0;
1252 }
1253
1254 // if there is non-whitespace before the ![ within the scope of two new lines, the image
1255 // is considered inlined, i.e. the image is not preceded by an empty line
1256 int numNLsNeeded=2;
1257 int pos = -1;
1258 while (pos>=-static_cast<int>(offset) && numNLsNeeded>0)
1259 {
1260 if (data.data()[pos]=='\n') numNLsNeeded--;
1261 else if (data.data()[pos]!=' ') // found non-whitespace, stop searching
1262 {
1263 isImageInline=true;
1264 break;
1265 }
1266 pos--;
1267 }
1268 // skip '!['
1269 i++;
1270 }
1271 size_t contentStart=i;
1272 int level=1;
1273 int nlTotal=0;
1274 int nl=0;
1275 // find the matching ]
1276 while (i<size)
1277 {
1278 if (data[i-1]=='\\') // skip escaped characters
1279 {
1280 }
1281 else if (data[i]=='[')
1282 {
1283 level++;
1284 }
1285 else if (data[i]==']')
1286 {
1287 level--;
1288 if (level<=0) break;
1289 }
1290 else if (data[i]=='\n')
1291 {
1292 nl++;
1293 if (nl>1) { return 0; } // only allow one newline in the content
1294 }
1295 i++;
1296 }
1297 nlTotal += nl;
1298 nl = 0;
1299 if (i>=size) return 0; // premature end of comment -> no link
1300 size_t contentEnd=i;
1301 content = data.substr(contentStart,contentEnd-contentStart);
1302 //printf("processLink: content={%s}\n",qPrint(content));
1303 if (!isImageLink && content.isEmpty()) { return 0; } // no link text
1304 i++; // skip over ]
1305
1306 bool whiteSpace = false;
1307 // skip whitespace
1308 while (i<size && data[i]==' ') { whiteSpace = true; i++; }
1309 if (i<size && data[i]=='\n') // one newline allowed here
1310 {
1311 whiteSpace = true;
1312 i++;
1313 // skip more whitespace
1314 while (i<size && data[i]==' ') i++;
1315 }
1316 if (whiteSpace && i<size && (data[i]=='(' || data[i]=='[')) return 0;
1317
1318 bool explicitTitle=FALSE;
1319 if (i<size && data[i]=='(') // inline link
1320 {
1321 i++;
1322 while (i<size && data[i]==' ') i++;
1323 bool uriFormat=false;
1324 if (i<size && data[i]=='<') { i++; uriFormat=true; }
1325 size_t linkStart=i;
1326 int braceCount=1;
1327 int nlConsec = 0;
1328 while (i<size && data[i]!='\'' && data[i]!='"' && braceCount>0)
1329 {
1330 if (data[i]=='\n') // unexpected EOL
1331 {
1332 nl++;
1333 nlConsec++;
1334 if (nlConsec>1) { return 0; }
1335 }
1336 else if (data[i]=='(')
1337 {
1338 braceCount++;
1339 nlConsec = 0;
1340 }
1341 else if (data[i]==')')
1342 {
1343 braceCount--;
1344 nlConsec = 0;
1345 }
1346 else if (data[i]!=' ')
1347 {
1348 nlConsec = 0;
1349 }
1350 if (braceCount>0)
1351 {
1352 i++;
1353 }
1354 }
1355 nlTotal += nl;
1356 nl = 0;
1357 if (i>=size || data[i]=='\n') { return 0; }
1358 link = data.substr(linkStart,i-linkStart);
1359 link = link.stripWhiteSpace();
1360 //printf("processLink: link={%s}\n",qPrint(link));
1361 if (link.isEmpty()) { return 0; }
1362 if (uriFormat && link.at(link.length()-1)=='>') link=link.left(link.length()-1);
1363
1364 // optional title
1365 if (data[i]=='\'' || data[i]=='"')
1366 {
1367 char c = data[i];
1368 i++;
1369 size_t titleStart=i;
1370 nl=0;
1371 while (i<size)
1372 {
1373 if (data[i]=='\n')
1374 {
1375 if (nl>1) { return 0; }
1376 nl++;
1377 }
1378 else if (data[i]=='\\') // escaped char in string
1379 {
1380 i++;
1381 }
1382 else if (data[i]==c)
1383 {
1384 i++;
1385 break;
1386 }
1387 i++;
1388 }
1389 if (i>=size)
1390 {
1391 return 0;
1392 }
1393 size_t titleEnd = i-1;
1394 // search back for closing marker
1395 while (titleEnd>titleStart && data[titleEnd]==' ') titleEnd--;
1396 if (data[titleEnd]==c) // found it
1397 {
1398 title = data.substr(titleStart,titleEnd-titleStart);
1399 explicitTitle=TRUE;
1400 while (i<size)
1401 {
1402 if (data[i]==' ')i++; // remove space after the closing quote and the closing bracket
1403 else if (data[i] == ')') break; // the end bracket
1404 else // illegal
1405 {
1406 return 0;
1407 }
1408 }
1409 }
1410 else
1411 {
1412 return 0;
1413 }
1414 }
1415 i++;
1416 }
1417 else if (i<size && data[i]=='[') // reference link
1418 {
1419 i++;
1420 size_t linkStart=i;
1421 nl=0;
1422 // find matching ]
1423 while (i<size && data[i]!=']')
1424 {
1425 if (data[i]=='\n')
1426 {
1427 nl++;
1428 if (nl>1) { return 0; }
1429 }
1430 i++;
1431 }
1432 if (i>=size) { return 0; }
1433 // extract link
1434 link = data.substr(linkStart,i-linkStart);
1435 //printf("processLink: link={%s}\n",qPrint(link));
1436 link = link.stripWhiteSpace();
1437 if (link.isEmpty()) // shortcut link
1438 {
1439 link=content;
1440 }
1441 // lookup reference
1442 QCString link_lower = link.lower();
1443 auto lr_it=linkRefs.find(link_lower.str());
1444 if (lr_it!=linkRefs.end()) // found it
1445 {
1446 link = lr_it->second.link;
1447 title = lr_it->second.title;
1448 //printf("processLink: ref: link={%s} title={%s}\n",qPrint(link),qPrint(title));
1449 }
1450 else // reference not found!
1451 {
1452 //printf("processLink: ref {%s} do not exist\n",link.qPrint(lower()));
1453 return 0;
1454 }
1455 i++;
1456 }
1457 else if (i<size && data[i]!=':' && !content.isEmpty()) // minimal link ref notation [some id]
1458 {
1459 QCString content_lower = content.lower();
1460 auto lr_it = linkRefs.find(content_lower.str());
1461 //printf("processLink: minimal link {%s} lr=%p",qPrint(content),lr);
1462 if (lr_it!=linkRefs.end()) // found it
1463 {
1464 link = lr_it->second.link;
1465 title = lr_it->second.title;
1466 explicitTitle=TRUE;
1467 i=contentEnd;
1468 }
1469 else if (content=="TOC")
1470 {
1471 isToc=TRUE;
1472 i=contentEnd;
1473 }
1474 else
1475 {
1476 return 0;
1477 }
1478 i++;
1479 }
1480 else
1481 {
1482 return 0;
1483 }
1484 nlTotal += nl;
1485
1486 // search for optional image attributes
1487 QCString attributes;
1488 if (isImageLink)
1489 {
1490 size_t j = i;
1491 // skip over whitespace
1492 while (j<size && data[j]==' ') { j++; }
1493 if (j<size && data[j]=='{') // we have attributes
1494 {
1495 i = j;
1496 // skip over '{'
1497 i++;
1498 size_t attributesStart=i;
1499 nl=0;
1500 // find the matching '}'
1501 while (i<size)
1502 {
1503 if (data[i-1]=='\\') // skip escaped characters
1504 {
1505 }
1506 else if (data[i]=='{')
1507 {
1508 level++;
1509 }
1510 else if (data[i]=='}')
1511 {
1512 level--;
1513 if (level<=0) break;
1514 }
1515 else if (data[i]=='\n')
1516 {
1517 nl++;
1518 if (nl>1) { return 0; } // only allow one newline in the content
1519 }
1520 i++;
1521 }
1522 nlTotal += nl;
1523 if (i>=size) return 0; // premature end of comment -> no attributes
1524 size_t attributesEnd=i;
1525 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1526 i++; // skip over '}'
1527 }
1528 if (!isImageInline)
1529 {
1530 // if there is non-whitespace after the image within the scope of two new lines, the image
1531 // is considered inlined, i.e. the image is not followed by an empty line
1532 int numNLsNeeded=2;
1533 size_t pos = i;
1534 while (pos<size && numNLsNeeded>0)
1535 {
1536 if (data[pos]=='\n') numNLsNeeded--;
1537 else if (data[pos]!=' ') // found non-whitespace, stop searching
1538 {
1539 isImageInline=true;
1540 break;
1541 }
1542 pos++;
1543 }
1544 }
1545 }
1546
1547 if (isToc) // special case for [TOC]
1548 {
1549 int toc_level = Config_getInt(TOC_INCLUDE_HEADINGS);
1550 if (toc_level>=SectionType::MinLevel && toc_level<=SectionType::MaxLevel)
1551 {
1552 out+="@tableofcontents{html:";
1553 out+=QCString().setNum(toc_level);
1554 out+="}";
1555 }
1556 }
1557 else if (isImageLink)
1558 {
1559 bool ambig = false;
1560 FileDef *fd=nullptr;
1561 if (link.find("@ref ")!=-1 || link.find("\\ref ")!=-1 ||
1563 // assume doxygen symbol link or local image link
1564 {
1565 // check if different handling is needed per format
1566 writeMarkdownImage("html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1567 writeMarkdownImage("latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1568 writeMarkdownImage("rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1569 writeMarkdownImage("docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1570 writeMarkdownImage("xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1571 }
1572 else
1573 {
1574 out+="<img src=\"";
1575 out+=link;
1576 out+="\" alt=\"";
1577 out+=content;
1578 out+="\"";
1579 if (!title.isEmpty())
1580 {
1581 out+=" title=\"";
1582 out+=substitute(title.simplifyWhiteSpace(),"\"","&quot;");
1583 out+="\"";
1584 }
1585 out+="/>";
1586 }
1587 }
1588 else
1589 {
1591 int lp=-1;
1592 if ((lp=link.find("@ref "))!=-1 || (lp=link.find("\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !isURL(link)))
1593 // assume doxygen symbol link
1594 {
1595 if (lp==-1) // link to markdown page
1596 {
1597 out+="@ref \"";
1598 if (!(Portable::isAbsolutePath(link) || isURL(link)))
1599 {
1600 FileInfo forg(link.str());
1601 if (forg.exists() && forg.isReadable())
1602 {
1603 link = forg.absFilePath();
1604 }
1605 else if (!(forg.exists() && forg.isReadable()))
1606 {
1607 FileInfo fi(fileName.str());
1608 QCString mdFile = fileName.left(fileName.length()-fi.fileName().length()) + link;
1609 FileInfo fmd(mdFile.str());
1610 if (fmd.exists() && fmd.isReadable())
1611 {
1612 link = fmd.absFilePath().data();
1613 }
1614 }
1615 }
1616 out+=link;
1617 out+="\"";
1618 }
1619 else
1620 {
1621 out+=link;
1622 }
1623 out+=" \"";
1624 if (explicitTitle && !title.isEmpty())
1625 {
1626 out+=substitute(title,"\"","&quot;");
1627 }
1628 else
1629 {
1630 processInline(std::string_view(substitute(content,"\"","&quot;").str()));
1631 }
1632 out+="\"";
1633 }
1634 else if ((lp=link.find('#'))!=-1 || link.find('/')!=-1 || link.find('.')!=-1)
1635 { // file/url link
1636 if (lp==0 || (lp>0 && !isURL(link) && Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1637 {
1638 out+="@ref \"";
1640 out+="\" \"";
1641 out+=substitute(content.simplifyWhiteSpace(),"\"","&quot;");
1642 out+="\"";
1643 }
1644 else
1645 {
1646 out+="<a href=\"";
1647 out+=link;
1648 out+="\"";
1649 for (int ii = 0; ii < nlTotal; ii++) out+="\n";
1650 if (!title.isEmpty())
1651 {
1652 out+=" title=\"";
1653 out+=substitute(title.simplifyWhiteSpace(),"\"","&quot;");
1654 out+="\"";
1655 }
1656 out+=" ";
1658 out+=">";
1659 content = content.simplifyWhiteSpace();
1660 processInline(std::string_view(content.str()));
1661 out+="</a>";
1662 }
1663 }
1664 else // avoid link to e.g. F[x](y)
1665 {
1666 //printf("no link for '%s'\n",qPrint(link));
1667 return 0;
1668 }
1669 }
1670 AUTO_TRACE_EXIT("result={}",i);
1671 return static_cast<int>(i);
1672}
static std::string addPrefixIfNeeded(const std::string &anchor)
Definition anchor.cpp:46
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
QCString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple whitespace cha...
Definition qcstring.cpp:190
static constexpr int MinLevel
Definition section.h:32
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:498
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)
SrcLangExt
Definition types.h:207
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5147
bool isURL(const QCString &url)
Checks whether the given url starts with a supported protocol.
Definition util.cpp:5865
QCString externalLinkTarget(const bool parent)
Definition util.cpp:5661
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2838

References FileInfo::absFilePath(), AnchorGenerator::addPrefixIfNeeded(), QCString::at(), AUTO_TRACE, AUTO_TRACE_EXIT, Config_getEnum, Config_getInt, FileInfo::exists(), externalLinkTarget(), FALSE, FileInfo::fileName(), fileName, QCString::find(), findFileDef(), getLanguageFromFileName(), Doxygen::imageNameLinkedMap, Portable::isAbsolutePath(), QCString::isEmpty(), FileInfo::isReadable(), isURL(), QCString::left(), QCString::length(), linkRefs, QCString::lower(), SectionType::MaxLevel, QCString::mid(), SectionType::MinLevel, out, processInline(), QCString::setNum(), QCString::simplifyWhiteSpace(), QCString::str(), QCString::stripWhiteSpace(), substitute(), TRUE, Trace::trunc(), and writeMarkdownImage().

Referenced by Markdown::fill_table().

◆ processNmdash()

int Markdown::Private::processNmdash ( std::string_view data,
size_t offset )

Process ndash and mdashes.

Definition at line 975 of file markdown.cpp.

976{
977 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
978 const size_t size = data.size();
979 // precondition: data[0]=='-'
980 size_t i=1;
981 int count=1;
982 if (i<size && data[i]=='-') // found --
983 {
984 count++;
985 i++;
986 }
987 if (i<size && data[i]=='-') // found ---
988 {
989 count++;
990 i++;
991 }
992 if (i<size && data[i]=='-') // found ----
993 {
994 count++;
995 }
996 if (count>=2 && offset>=2 && literal_at(data.data()-2,"<!"))
997 { AUTO_TRACE_EXIT("result={}",1-count); return 1-count; } // start HTML comment
998 if (count==2 && size > 2 && data[2]=='>')
999 { return 0; } // end HTML comment
1000 if (count==3 && size > 3 && data[3]=='>')
1001 { return 0; } // end HTML comment
1002 if (count==2 && (offset<8 || !literal_at(data.data()-8,"operator"))) // -- => ndash
1003 {
1004 out+="&ndash;";
1005 AUTO_TRACE_EXIT("result=2");
1006 return 2;
1007 }
1008 else if (count==3) // --- => ndash
1009 {
1010 out+="&mdash;";
1011 AUTO_TRACE_EXIT("result=3");
1012 return 3;
1013 }
1014 // not an ndash or mdash
1015 return 0;
1016}

References AUTO_TRACE, AUTO_TRACE_EXIT, literal_at(), out, and Trace::trunc().

Referenced by Markdown::fill_table().

◆ processQuotations()

QCString Markdown::Private::processQuotations ( std::string_view data,
size_t refIndent )

Definition at line 3222 of file markdown.cpp.

3223{
3224 AUTO_TRACE("data='{}' refIndex='{}'",Trace::trunc(data),refIndent);
3225 out.clear();
3226 size_t i=0,end=0;
3227 size_t pi=std::string::npos;
3228 bool newBlock = false;
3229 bool insideList = false;
3230 size_t currentIndent = refIndent;
3231 size_t listIndent = refIndent;
3232 const size_t size = data.size();
3233 QCString lang;
3234 while (i<size)
3235 {
3236 end = findEndOfLine(data,i);
3237 // line is now found at [i..end)
3238
3239 size_t lineIndent=0;
3240 while (lineIndent<end && data[i+lineIndent]==' ') lineIndent++;
3241 //printf("** lineIndent=%d line=(%s)\n",lineIndent,qPrint(QCString(data+i).left(end-i)));
3242
3243 if (newBlock)
3244 {
3245 //printf("** end of block\n");
3246 if (insideList && lineIndent<currentIndent) // end of list
3247 {
3248 //printf("** end of list\n");
3249 currentIndent = refIndent;
3250 insideList = false;
3251 }
3252 newBlock = false;
3253 }
3254
3255 if ((listIndent=isListMarker(data.substr(i,end-i)))) // see if we need to increase the indent level
3256 {
3257 if (listIndent<currentIndent+4)
3258 {
3259 //printf("** start of list\n");
3260 insideList = true;
3261 currentIndent = listIndent;
3262 }
3263 }
3264 else if (isEndOfList(data.substr(i,end-i)))
3265 {
3266 //printf("** end of list\n");
3267 insideList = false;
3268 currentIndent = listIndent;
3269 }
3270 else if (isEmptyLine(data.substr(i,end-i)))
3271 {
3272 //printf("** new block\n");
3273 newBlock = true;
3274 }
3275 //printf("currentIndent=%d listIndent=%d refIndent=%d\n",currentIndent,listIndent,refIndent);
3276
3277 if (pi!=std::string::npos)
3278 {
3279 size_t blockStart=0, blockEnd=0, blockOffset=0;
3280 if (isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset,fileName,lineNr))
3281 {
3282 auto addSpecialCommand = [&](const QCString &startCmd,const QCString &endCmd)
3283 {
3284 size_t cmdPos = pi+blockStart+1;
3285 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3286 size_t ii = 0;
3287 int nl = 1;
3288 // check for absence of start command, either @start<cmd>, or \\start<cmd>
3289 while (ii<pl.length() && qisspace(pl[ii]))
3290 {
3291 if (pl[ii]=='\n') nl++;
3292 ii++; // skip leading whitespace
3293 }
3294 bool addNewLines = false;
3295 if (ii+startCmd.length()>=pl.length() || // no room for start command
3296 (pl[ii]!='\\' && pl[ii]!='@') || // no @ or \ after whitespace
3297 qstrncmp(pl.data()+ii+1,startCmd.data(),startCmd.length())!=0) // no start command
3298 {
3299 // input: output:
3300 // ----------------------------------------------------
3301 // ```{plantuml} => @startuml
3302 // A->B A->B
3303 // ``` @enduml
3304 // ----------------------------------------------------
3305 pl = "@"+startCmd+"\n" + pl + "@"+endCmd;
3306 addNewLines = false;
3307 }
3308 else // we have a @start... command inside the code block
3309 {
3310 // input: output:
3311 // ----------------------------------------------------
3312 // ```{plantuml} \n
3313 // \n
3314 // @startuml => @startuml
3315 // A->B A->B
3316 // @enduml @enduml
3317 // ``` \n
3318 // ----------------------------------------------------
3319 addNewLines = true;
3320 }
3321 if (addNewLines) for (int j=0;j<nl;j++) out+='\n';
3322 processSpecialCommand(pl.view().substr(ii),ii);
3323 if (addNewLines) out+='\n';
3324 };
3325
3326 if (!Config_getString(PLANTUML_JAR_PATH).isEmpty() && lang=="plantuml")
3327 {
3328 addSpecialCommand("startuml","enduml");
3329 }
3330 else if (Config_getBool(HAVE_DOT) && lang=="dot")
3331 {
3332 addSpecialCommand("dot","enddot");
3333 }
3334 else if (lang=="msc") // msc is built-in
3335 {
3336 addSpecialCommand("msc","endmsc");
3337 }
3338 else // normal code block
3339 {
3340 writeFencedCodeBlock(data.substr(pi),lang.view(),blockStart,blockEnd);
3341 }
3342 i=pi+blockOffset;
3343 pi=std::string::npos;
3344 end=i+1;
3345 continue;
3346 }
3347 else if (isBlockQuote(data.substr(pi,i-pi),currentIndent))
3348 {
3349 i = pi+writeBlockQuote(data.substr(pi));
3350 pi=std::string::npos;
3351 end=i+1;
3352 continue;
3353 }
3354 else
3355 {
3356 //printf("quote out={%s}\n",QCString(data+pi).left(i-pi).data());
3357 out+=data.substr(pi,i-pi);
3358 }
3359 }
3360 pi=i;
3361 i=end;
3362 }
3363 if (pi!=std::string::npos && pi<size) // deal with the last line
3364 {
3365 if (isBlockQuote(data.substr(pi),currentIndent))
3366 {
3367 writeBlockQuote(data.substr(pi));
3368 }
3369 else
3370 {
3371 if (QCString(data.substr(pi)).startsWith("```") || QCString(data.substr(pi)).startsWith("~~~"))
3372 {
3373 warn(fileName, lineNr, "Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3374 }
3375 out+=data.substr(pi);
3376 }
3377 }
3378
3379 //printf("Process quotations\n---- input ----\n%s\n---- output ----\n%s\n------------\n",
3380 // qPrint(s),prv->out.get());
3381
3382 return out;
3383}
#define Config_getString(name)
Definition config.h:32
static bool isBlockQuote(std::string_view data, size_t indent)
returns true if this line starts a block quote
bool qisspace(char c)
Definition qcstring.h:81
size_t writeBlockQuote(std::string_view data)
int processSpecialCommand(std::string_view data, size_t offset)

References AUTO_TRACE, Config_getBool, Config_getString, QCString::data(), end(), fileName, findEndOfLine(), isBlockQuote(), isEmptyLine(), isEndOfList(), isFencedCodeBlock(), isListMarker(), QCString::length(), lineNr, out, processSpecialCommand(), qisspace(), qstrncmp(), QCString::startsWith(), Trace::trunc(), QCString::view(), warn, writeBlockQuote(), and writeFencedCodeBlock().

◆ processQuoted()

int Markdown::Private::processQuoted ( std::string_view data,
size_t offset )

Process quoted section "...", can contain one embedded newline.

Definition at line 1019 of file markdown.cpp.

1020{
1021 AUTO_TRACE("data='{}'",Trace::trunc(data));
1022 const size_t size = data.size();
1023 size_t i=1;
1024 int nl=0;
1025 while (i<size && data[i]!='"' && nl<2)
1026 {
1027 if (data[i]=='\n') nl++;
1028 i++;
1029 }
1030 if (i<size && data[i]=='"' && nl<2)
1031 {
1032 out+=data.substr(0,i+1);
1033 AUTO_TRACE_EXIT("result={}",i+2);
1034 return static_cast<int>(i+1);
1035 }
1036 // not a quoted section
1037 return 0;
1038}

References AUTO_TRACE, AUTO_TRACE_EXIT, out, and Trace::trunc().

Referenced by Markdown::fill_table().

◆ processSpecialCommand()

int Markdown::Private::processSpecialCommand ( std::string_view data,
size_t offset )

Definition at line 1802 of file markdown.cpp.

1803{
1804 AUTO_TRACE("{}",Trace::trunc(data));
1805 const size_t size = data.size();
1806 size_t i=1;
1807 QCString endBlockName = isBlockCommand(data,offset);
1808 if (!endBlockName.isEmpty())
1809 {
1810 AUTO_TRACE_ADD("endBlockName={}",endBlockName);
1811 size_t l = endBlockName.length();
1812 while (i+l<size)
1813 {
1814 if ((data[i]=='\\' || data[i]=='@') && // command
1815 data[i-1]!='\\' && data[i-1]!='@') // not escaped
1816 {
1817 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
1818 {
1819 //printf("found end at %d\n",i);
1820 addStrEscapeUtf8Nbsp(data.substr(0,i+1+l));
1821 AUTO_TRACE_EXIT("result={}",i+1+l);
1822 return static_cast<int>(i+1+l);
1823 }
1824 }
1825 i++;
1826 }
1827 }
1828 size_t endPos = isSpecialCommand(data,offset);
1829 if (endPos>0)
1830 {
1831 out+=data.substr(0,endPos);
1832 return static_cast<int>(endPos);
1833 }
1834 if (size>1 && (data[0]=='\\' || data[0]=='@')) // escaped characters
1835 {
1836 char c=data[1];
1837 if (c=='[' || c==']' || c=='*' || c=='(' || c==')' || c=='`' || c=='_')
1838 {
1839 out+=data[1];
1840 AUTO_TRACE_EXIT("2");
1841 return 2;
1842 }
1843 else if (c=='\\' || c=='@')
1844 {
1845 out+=data.substr(0,2);
1846 AUTO_TRACE_EXIT("2");
1847 return 2;
1848 }
1849 else if (c=='-' && size>3 && data[2]=='-' && data[3]=='-') // \---
1850 {
1851 out+=data.substr(1,3);
1852 AUTO_TRACE_EXIT("2");
1853 return 4;
1854 }
1855 else if (c=='-' && size>2 && data[2]=='-') // \--
1856 {
1857 out+=data.substr(1,2);
1858 AUTO_TRACE_EXIT("3");
1859 return 3;
1860 }
1861 }
1862 return 0;
1863}
size_t isSpecialCommand(std::string_view data, size_t offset)
Definition markdown.cpp:457
void addStrEscapeUtf8Nbsp(std::string_view data)

References addStrEscapeUtf8Nbsp(), AUTO_TRACE, AUTO_TRACE_ADD, AUTO_TRACE_EXIT, QCString::data(), isBlockCommand(), QCString::isEmpty(), isSpecialCommand(), QCString::length(), out, qstrncmp(), and Trace::trunc().

Referenced by Markdown::fill_table(), and processQuotations().

◆ writeBlockQuote()

size_t Markdown::Private::writeBlockQuote ( std::string_view data)

Definition at line 2891 of file markdown.cpp.

2892{
2893 AUTO_TRACE("data='{}'",Trace::trunc(data));
2894 size_t i=0;
2895 int curLevel=0;
2896 size_t end=0;
2897 const size_t size = data.size();
2898 std::string startCmd;
2899 int isGitHubAlert = false;
2900 int isGitHubFirst = false;
2901 while (i<size)
2902 {
2903 // find end of this line
2904 end=i+1;
2905 while (end<=size && data[end-1]!='\n') end++;
2906 size_t j=i;
2907 int level=0;
2908 size_t indent=i;
2909 // compute the quoting level
2910 while (j<end && (data[j]==' ' || data[j]=='>'))
2911 {
2912 if (data[j]=='>') { level++; indent=j+1; }
2913 else if (j>0 && data[j-1]=='>') indent=j+1;
2914 j++;
2915 }
2916 if (indent>0 && j>0 && data[j-1]=='>' &&
2917 !(j==size || data[j]=='\n')) // disqualify last > if not followed by space
2918 {
2919 indent--;
2920 level--;
2921 j--;
2922 }
2923 AUTO_TRACE_ADD("indent={} i={} j={} end={} level={} line={}",indent,i,j,end,level,Trace::trunc(&data[i]));
2924 if (level==0 && j<end-1 && !isListMarker(data.substr(j)) && !isHRuler(data.substr(j)))
2925 {
2926 level = curLevel; // lazy
2927 }
2928 if (level==1)
2929 {
2930 QCString txt = stripWhiteSpace(data.substr(indent,end-indent));
2931 auto it = g_quotationHeaderMap.find(txt.lower().str()); // TODO: in C++20 the std::string can be dropped
2932 if (it != g_quotationHeaderMap.end())
2933 {
2934 isGitHubAlert = true;
2935 isGitHubFirst = true;
2936 startCmd = it->second;
2937 }
2938 }
2939 if (level>curLevel) // quote level increased => add start markers
2940 {
2941 if (level!=1 || !isGitHubAlert) // normal block quote
2942 {
2943 for (int l=curLevel;l<level-1;l++)
2944 {
2945 out+="<blockquote>";
2946 }
2947 out += "<blockquote>&zwj;"; // empty blockquotes are also shown
2948 }
2949 else if (!startCmd.empty()) // GitHub style alert
2950 {
2951 out += startCmd + " ";
2952 }
2953 }
2954 else if (level<curLevel) // quote level decreased => add end markers
2955 {
2956 int decrLevel = curLevel;
2957 if (level==0 && isGitHubAlert)
2958 {
2959 decrLevel--;
2960 }
2961 for (int l=level;l<decrLevel;l++)
2962 {
2963 out += "</blockquote>\\ilinebr ";
2964 }
2965 }
2966 if (level==0)
2967 {
2968 curLevel=0;
2969 break; // end of quote block
2970 }
2971 // copy line without quotation marks
2972 if (curLevel!=0 || !isGitHubAlert)
2973 {
2974 std::string_view txt = data.substr(indent,end-indent);
2975 if (stripWhiteSpace(txt).empty() && !startCmd.empty())
2976 {
2977 if (!isGitHubFirst) out += "<br>";
2978 out += "<br>\n";
2979 }
2980 else
2981 {
2982 out += txt;
2983 }
2984 isGitHubFirst = false;
2985 }
2986 else // GitHub alert section
2987 {
2988 out+= "\n";
2989 }
2990 curLevel=level;
2991 // proceed with next line
2992 i=end;
2993 }
2994 // end of comment within blockquote => add end markers
2995 if (isGitHubAlert) // GitHub alert doesn't have a blockquote
2996 {
2997 curLevel--;
2998 }
2999 for (int l=0;l<curLevel;l++)
3000 {
3001 out+="</blockquote>";
3002 }
3003 AUTO_TRACE_EXIT("i={}",i);
3004 return i;
3005}
static const std::unordered_map< std::string, std::string > g_quotationHeaderMap
static bool isHRuler(std::string_view data)
static void decrLevel(yyscan_t yyscanner)
Definition pre.l:2251
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...
Definition stringutil.h:72

References AUTO_TRACE, AUTO_TRACE_ADD, AUTO_TRACE_EXIT, decrLevel(), end(), g_quotationHeaderMap, isHRuler(), isListMarker(), QCString::lower(), out, QCString::str(), stripWhiteSpace(), and Trace::trunc().

Referenced by processQuotations().

◆ writeCodeBlock()

size_t Markdown::Private::writeCodeBlock ( std::string_view data,
size_t refIndent )

Definition at line 3047 of file markdown.cpp.

3048{
3049 AUTO_TRACE("data='{}' refIndent={}",Trace::trunc(data),refIndent);
3050 const size_t size = data.size();
3051 size_t i=0;
3052 // no need for \ilinebr here as the previous line was empty and was skipped
3053 out+="@iverbatim\n";
3054 int emptyLines=0;
3055 std::string location;
3056 while (i<size)
3057 {
3058 // find end of this line
3059 size_t end=i+1;
3060 while (end<=size && data[end-1]!='\n') end++;
3061 size_t j=i;
3062 size_t indent=0;
3063 while (j < end && data[j] == ' ')
3064 {
3065 j++;
3066 indent++;
3067 }
3068 //printf("j=%d end=%d indent=%d refIndent=%d tabSize=%d data={%s}\n",
3069 // j,end,indent,refIndent,Config_getInt(TAB_SIZE),qPrint(QCString(data+i).left(end-i-1)));
3070 if (j==end-1) // empty line
3071 {
3072 emptyLines++;
3073 i=end;
3074 }
3075 else if (indent>=refIndent+codeBlockIndent) // enough indent to continue the code block
3076 {
3077 while (emptyLines>0) // write skipped empty lines
3078 {
3079 // add empty line
3080 out+="\n";
3081 emptyLines--;
3082 }
3083 // add code line minus the indent
3084 size_t offset = i+refIndent+codeBlockIndent;
3085 std::string lineLoc;
3086 if (skipOverFileAndLineCommands(data,codeBlockIndent,offset,lineLoc))
3087 {
3088 location = lineLoc;
3089 }
3090 out+=data.substr(offset,end-offset);
3091 i=end;
3092 }
3093 else // end of code block
3094 {
3095 break;
3096 }
3097 }
3098 out+="@endiverbatim";
3099 if (!location.empty())
3100 {
3101 out+=location;
3102 }
3103 else
3104 {
3105 out+="\\ilinebr ";
3106 }
3107 while (emptyLines>0) // write skipped empty lines
3108 {
3109 // add empty line
3110 out+="\n";
3111 emptyLines--;
3112 }
3113 AUTO_TRACE_EXIT("i={}",i);
3114 return i;
3115}
bool skipOverFileAndLineCommands(std::string_view data, size_t indent, size_t &offset, std::string &location)

References AUTO_TRACE, AUTO_TRACE_EXIT, codeBlockIndent, end(), out, skipOverFileAndLineCommands(), and Trace::trunc().

Referenced by processBlocks().

◆ writeFencedCodeBlock()

void Markdown::Private::writeFencedCodeBlock ( std::string_view data,
std::string_view lang,
size_t blockStart,
size_t blockEnd )

Definition at line 3199 of file markdown.cpp.

3201{
3202 AUTO_TRACE("data='{}' lang={} blockStart={} blockEnd={}",Trace::trunc(data),lang,blockStart,blockEnd);
3203 if (!lang.empty() && lang[0]=='.') lang=lang.substr(1);
3204 const size_t size=data.size();
3205 size_t i=0;
3206 while (i<size && (data[i]==' ' || data[i]=='\t'))
3207 {
3208 out+=data[i++];
3209 blockStart--;
3210 blockEnd--;
3211 }
3212 out+="@icode";
3213 if (!lang.empty())
3214 {
3215 out+="{"+lang+"}";
3216 }
3217 out+=" ";
3218 addStrEscapeUtf8Nbsp(data.substr(blockStart+i,blockEnd-blockStart));
3219 out+="@endicode ";
3220}

References addStrEscapeUtf8Nbsp(), AUTO_TRACE, out, and Trace::trunc().

Referenced by processBlocks(), and processQuotations().

◆ writeMarkdownImage()

void Markdown::Private::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 )

Definition at line 1192 of file markdown.cpp.

1197{
1198 AUTO_TRACE("fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1199 fmt,inline_img,explicitTitle,Trace::trunc(title),Trace::trunc(content),link,attrs);
1200 QCString attributes = getFilteredImageAttributes(fmt, attrs);
1201 out+="@image";
1202 if (inline_img)
1203 {
1204 out+="{inline}";
1205 }
1206 out+=" ";
1207 out+=fmt;
1208 out+=" ";
1209 out+=link.mid(fd ? 0 : 5);
1210 if (!explicitTitle && !content.isEmpty())
1211 {
1212 out+=" \"";
1213 out+=escapeDoubleQuotes(content);
1214 out+="\"";
1215 }
1216 else if ((content.isEmpty() || explicitTitle) && !title.isEmpty())
1217 {
1218 out+=" \"";
1219 out+=escapeDoubleQuotes(title);
1220 out+="\"";
1221 }
1222 else
1223 {
1224 out+=" ";// so the line break will not be part of the image name
1225 }
1226 if (!attributes.isEmpty())
1227 {
1228 out+=" ";
1229 out+=attributes;
1230 out+=" ";
1231 }
1232 out+="\\ilinebr ";
1233}
static QCString escapeDoubleQuotes(const QCString &s)
Definition markdown.cpp:238
static QCString getFilteredImageAttributes(std::string_view fmt, const QCString &attrs)
parse the image attributes and return attributes for given format
Definition markdown.cpp:341

References AUTO_TRACE, escapeDoubleQuotes(), getFilteredImageAttributes(), QCString::isEmpty(), QCString::mid(), out, and Trace::trunc().

Referenced by processLink().

◆ writeOneLineHeaderOrRuler()

void Markdown::Private::writeOneLineHeaderOrRuler ( std::string_view data)

Definition at line 2831 of file markdown.cpp.

2832{
2833 AUTO_TRACE("data='{}'",Trace::trunc(data));
2834 int level=0;
2835 QCString header;
2836 QCString id;
2837 if (isHRuler(data))
2838 {
2839 out+="<hr>\n";
2840 }
2841 else if ((level=isAtxHeader(data,header,id,TRUE)))
2842 {
2843 QCString hTag;
2844 if (!id.isEmpty())
2845 {
2846 switch (level)
2847 {
2848 case SectionType::Section: out+="@section "; break;
2849 case SectionType::Subsection: out+="@subsection "; break;
2850 case SectionType::Subsubsection: out+="@subsubsection "; break;
2851 case SectionType::Paragraph: out+="@paragraph "; break;
2852 case SectionType::Subparagraph: out+="@subparagraph "; break;
2853 case SectionType::Subsubparagraph: out+="@subsubparagraph "; break;
2854 }
2855 out+=id;
2856 out+=" ";
2857 out+=header;
2858 out+="\n";
2859 }
2860 else
2861 {
2862 hTag.sprintf("h%d",level);
2863 out+="<"+hTag+">";
2864 out+=header;
2865 out+="</"+hTag+">\n";
2866 }
2867 }
2868 else if (data.size()>0) // nothing interesting -> just output the line
2869 {
2870 size_t tmpSize = data.size();
2871 if (data[data.size()-1] == '\n') tmpSize--;
2872 out+=data.substr(0,tmpSize);
2873
2874 if (hasLineBreak(data))
2875 {
2876 out+="\\ilinebr<br>";
2877 }
2878 if (tmpSize != data.size()) out+='\n';
2879 }
2880}
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
static constexpr int Section
Definition section.h:33
static constexpr int Subsection
Definition section.h:34
static constexpr int Subsubsection
Definition section.h:35
static constexpr int Paragraph
Definition section.h:36
static constexpr int Subsubparagraph
Definition section.h:38
static constexpr int Subparagraph
Definition section.h:37
static bool hasLineBreak(std::string_view data)
int isAtxHeader(std::string_view data, QCString &header, QCString &id, bool allowAdjustLevel, bool *pIsIdGenerated=nullptr)

References AUTO_TRACE, hasLineBreak(), isAtxHeader(), isHRuler(), out, SectionType::Paragraph, SectionType::Section, QCString::sprintf(), SectionType::Subparagraph, SectionType::Subsection, SectionType::Subsubparagraph, SectionType::Subsubsection, TRUE, and Trace::trunc().

Referenced by processBlocks().

◆ writeTableBlock()

size_t Markdown::Private::writeTableBlock ( std::string_view data)

Definition at line 2612 of file markdown.cpp.

2613{
2614 AUTO_TRACE("data='{}'",Trace::trunc(data));
2615 const size_t size = data.size();
2616
2617 size_t columns=0, start=0, end=0;
2618 size_t i = findTableColumns(data,start,end,columns);
2619 size_t headerStart = start;
2620 size_t headerEnd = end;
2621
2622 // read cell alignments
2623 size_t cc = 0;
2624 size_t ret = findTableColumns(data.substr(i),start,end,cc);
2625 size_t k=0;
2626 std::vector<Alignment> columnAlignment(columns);
2627
2628 bool leftMarker=false, rightMarker=false, startFound=false;
2629 size_t j=start+i;
2630 while (j<=end+i)
2631 {
2632 if (!startFound)
2633 {
2634 if (data[j]==':') { leftMarker=TRUE; startFound=TRUE; }
2635 if (data[j]=='-') startFound=TRUE;
2636 //printf(" data[%d]=%c startFound=%d\n",j,data[j],startFound);
2637 }
2638 if (data[j]=='-') rightMarker=FALSE;
2639 else if (data[j]==':') rightMarker=TRUE;
2640 if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2641 {
2642 if (k<columns)
2643 {
2644 columnAlignment[k] = markersToAlignment(leftMarker,rightMarker);
2645 //printf("column[%d] alignment=%d\n",k,columnAlignment[k]);
2646 leftMarker=FALSE;
2647 rightMarker=FALSE;
2648 startFound=FALSE;
2649 }
2650 k++;
2651 }
2652 j++;
2653 }
2654 if (k<columns)
2655 {
2656 columnAlignment[k] = markersToAlignment(leftMarker,rightMarker);
2657 //printf("column[%d] alignment=%d\n",k,columnAlignment[k]);
2658 }
2659 // proceed to next line
2660 i+=ret;
2661
2662 // Store the table cell information by row then column. This
2663 // allows us to handle row spanning.
2664 std::vector<std::vector<TableCell> > tableContents;
2665
2666 size_t m = headerStart;
2667 std::vector<TableCell> headerContents(columns);
2668 for (k=0;k<columns;k++)
2669 {
2670 while (m<=headerEnd && (data[m]!='|' || (m>0 && data[m-1]=='\\')))
2671 {
2672 headerContents[k].cellText += data[m++];
2673 }
2674 m++;
2675 // do the column span test before stripping white space
2676 // || is spanning columns, | | is not
2677 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2678 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2679 }
2680 tableContents.push_back(headerContents);
2681
2682 // write table cells
2683 while (i<size)
2684 {
2685 ret = findTableColumns(data.substr(i),start,end,cc);
2686 if (cc!=columns) break; // end of table
2687
2688 j=start+i;
2689 k=0;
2690 std::vector<TableCell> rowContents(columns);
2691 while (j<=end+i)
2692 {
2693 if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2694 {
2695 // do the column span test before stripping white space
2696 // || is spanning columns, | | is not
2697 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2698 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2699 k++;
2700 } // if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2701 else
2702 {
2703 rowContents[k].cellText += data[j];
2704 } // else { if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\'))) }
2705 j++;
2706 } // while (j<=end+i)
2707 // do the column span test before stripping white space
2708 // || is spanning columns, | | is not
2709 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2710 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2711 tableContents.push_back(rowContents);
2712
2713 // proceed to next line
2714 i+=ret;
2715 }
2716
2717 out+="<table class=\"markdownTable\">";
2718 QCString cellTag("th"), cellClass("class=\"markdownTableHead");
2719 for (size_t row = 0; row < tableContents.size(); row++)
2720 {
2721 if (row)
2722 {
2723 if (row % 2)
2724 {
2725 out+="\n<tr class=\"markdownTableRowOdd\">";
2726 }
2727 else
2728 {
2729 out+="\n<tr class=\"markdownTableRowEven\">";
2730 }
2731 }
2732 else
2733 {
2734 out+="\n <tr class=\"markdownTableHead\">";
2735 }
2736 for (size_t c = 0; c < columns; c++)
2737 {
2738 // save the cell text for use after column span computation
2739 QCString cellText(tableContents[row][c].cellText);
2740
2741 // Row span handling. Spanning rows will contain a caret ('^').
2742 // If the current cell contains just a caret, this is part of an
2743 // earlier row's span and the cell should not be added to the
2744 // output.
2745 if (tableContents[row][c].cellText == "^")
2746 {
2747 continue;
2748 }
2749 if (tableContents[row][c].colSpan)
2750 {
2751 int cr = static_cast<int>(c);
2752 while ( cr >= 0 && tableContents[row][cr].colSpan)
2753 {
2754 cr--;
2755 };
2756 if (cr >= 0 && tableContents[row][cr].cellText == "^") continue;
2757 }
2758 size_t rowSpan = 1, spanRow = row+1;
2759 while ((spanRow < tableContents.size()) &&
2760 (tableContents[spanRow][c].cellText == "^"))
2761 {
2762 spanRow++;
2763 rowSpan++;
2764 }
2765
2766 out+=" <" + cellTag + " " + cellClass;
2767 // use appropriate alignment style
2768 switch (columnAlignment[c])
2769 {
2770 case Alignment::Left: out+="Left\""; break;
2771 case Alignment::Right: out+="Right\""; break;
2772 case Alignment::Center: out+="Center\""; break;
2773 case Alignment::None: out+="None\""; break;
2774 }
2775
2776 if (rowSpan > 1)
2777 {
2778 QCString spanStr;
2779 spanStr.setNum(rowSpan);
2780 out+=" rowspan=\"" + spanStr + "\"";
2781 }
2782 // Column span handling, assumes that column spans will have
2783 // empty strings, which would indicate the sequence "||", used
2784 // to signify spanning columns.
2785 size_t colSpan = 1;
2786 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2787 {
2788 c++;
2789 colSpan++;
2790 }
2791 if (colSpan > 1)
2792 {
2793 QCString spanStr;
2794 spanStr.setNum(colSpan);
2795 out+=" colspan=\"" + spanStr + "\"";
2796 }
2797 // need at least one space on either side of the cell text in
2798 // order for doxygen to do other formatting
2799 out+="> " + cellText + " \\ilinebr </" + cellTag + ">";
2800 }
2801 cellTag = "td";
2802 cellClass = "class=\"markdownTableBody";
2803 out+=" </tr>";
2804 }
2805 out+="</table>\n";
2806
2807 AUTO_TRACE_EXIT("i={}",i);
2808 return i;
2809}
QCString & setNum(short n)
Definition qcstring.h:459
static constexpr Alignment markersToAlignment(bool leftMarker, bool rightMarker)
helper function to convert presence of left and/or right alignment markers to an alignment value
Definition markdown.cpp:320
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.

References AUTO_TRACE, AUTO_TRACE_EXIT, Center, end(), FALSE, findTableColumns(), Left, markersToAlignment(), None, out, Right, QCString::setNum(), TRUE, and Trace::trunc().

Referenced by processBlocks().

Member Data Documentation

◆ fileName

QCString Markdown::Private::fileName

Definition at line 176 of file markdown.cpp.

Referenced by extractTitleId(), Private(), processBlocks(), processLink(), and processQuotations().

◆ indentLevel

int Markdown::Private::indentLevel =0

Definition at line 178 of file markdown.cpp.

Referenced by isAtxHeader(), isHeaderline(), and Private().

◆ lineNr

int Markdown::Private::lineNr = 0

Definition at line 177 of file markdown.cpp.

Referenced by extractTitleId(), Private(), processBlocks(), and processQuotations().

◆ linkRefs

std::unordered_map<std::string,LinkRef> Markdown::Private::linkRefs

Definition at line 175 of file markdown.cpp.

Referenced by processBlocks(), and processLink().

◆ out


The documentation for this struct was generated from the following file: