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

Classes

struct  LinkRef

Public Types

using Action_t = std::function<int(std::string_view,size_t)>

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
std::array< Action_t, 256 > actions

Detailed Description

Definition at line 121 of file markdown.cpp.

Member Typedef Documentation

◆ Action_t

using Markdown::Private::Action_t = std::function<int(std::string_view,size_t)>

Definition at line 180 of file markdown.cpp.

Constructor & Destructor Documentation

◆ Private()

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

Definition at line 123 of file markdown.cpp.

124 : fileName(fn), lineNr(line), indentLevel(indent)
125 {
126 // setup callback table for special characters
127 actions[static_cast<unsigned int>('_')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
128 actions[static_cast<unsigned int>('*')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
129 actions[static_cast<unsigned int>('~')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
130 actions[static_cast<unsigned int>('`')] = [this](std::string_view data,size_t offset) { return processCodeSpan (data,offset); };
131 actions[static_cast<unsigned int>('\\')]= [this](std::string_view data,size_t offset) { return processSpecialCommand(data,offset); };
132 actions[static_cast<unsigned int>('@')] = [this](std::string_view data,size_t offset) { return processSpecialCommand(data,offset); };
133 actions[static_cast<unsigned int>('[')] = [this](std::string_view data,size_t offset) { return processLink (data,offset); };
134 actions[static_cast<unsigned int>('!')] = [this](std::string_view data,size_t offset) { return processLink (data,offset); };
135 actions[static_cast<unsigned int>('<')] = [this](std::string_view data,size_t offset) { return processHtmlTag (data,offset); };
136 actions[static_cast<unsigned int>('-')] = [this](std::string_view data,size_t offset) { return processNmdash (data,offset); };
137 actions[static_cast<unsigned int>('"')] = [this](std::string_view data,size_t offset) { return processQuoted (data,offset); };
138 }
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 processHtmlTag(std::string_view data, size_t offset)
int processEmphasis(std::string_view data, size_t offset)
int processLink(std::string_view data, size_t offset)
int processNmdash(std::string_view data, size_t offset)
Process ndash and mdashes.
Definition markdown.cpp:961
std::array< Action_t, 256 > actions
Definition markdown.cpp:187

Member Function Documentation

◆ addStrEscapeUtf8Nbsp()

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

Definition at line 1761 of file markdown.cpp.

1762{
1763 AUTO_TRACE("{}",Trace::trunc(data));
1764 if (Portable::strnstr(data.data(),g_doxy_nbsp,data.size())==nullptr) // no escape needed -> fast
1765 {
1766 out+=data;
1767 }
1768 else // escape needed -> slow
1769 {
1770 out+=substitute(QCString(data),g_doxy_nbsp,g_utf8_nbsp);
1771 }
1772}
#define AUTO_TRACE(...)
Definition docnode.cpp:46
const char * g_doxy_nbsp
Definition markdown.cpp:208
const char * g_utf8_nbsp
Definition markdown.cpp:207
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:482

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 2064 of file markdown.cpp.

2065{
2066 AUTO_TRACE("title={} level={}",Trace::trunc(title),level);
2067 // match e.g. '{#id-b11} ' and capture 'id-b11'
2068 static const reg::Ex r2(R"({#(\a[\w-]*)}\s*$)");
2069 reg::Match match;
2070 std::string ti = title.str();
2071 if (reg::search(ti,match,r2))
2072 {
2073 std::string id = match[1].str();
2074 title = title.left(match.position());
2075 if (AnchorGenerator::instance().reserve(id)>0)
2076 {
2077 warn(fileName, lineNr, "An automatically generated id already has the name '{}'!", id);
2078 }
2079 //printf("found match id='%s' title=%s\n",qPrint(id),qPrint(title));
2080 AUTO_TRACE_EXIT("id={}",id);
2081 return id;
2082 }
2083 if (((level>0) && (level<=Config_getInt(TOC_INCLUDE_HEADINGS))) || (Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2084 {
2085 QCString id = AnchorGenerator::instance().generate(ti);
2086 if (pIsIdGenerated) *pIsIdGenerated=true;
2087 //printf("auto-generated id='%s' title='%s'\n",qPrint(id),qPrint(title));
2088 AUTO_TRACE_EXIT("id={}",id);
2089 return id;
2090 }
2091 //printf("no id found in title '%s'\n",qPrint(title));
2092 return "";
2093}
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:59
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:748
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:759

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 720 of file markdown.cpp.

721{
722 AUTO_TRACE("data='{}' c={} c_size={}",Trace::trunc(data),c,c_size);
723 size_t i = 1;
724 const size_t size = data.size();
725
726 while (i<size)
727 {
728 while (i<size && data[i]!=c &&
729 data[i]!='\\' && data[i]!='@' &&
730 !(data[i]=='/' && data[i-1]=='<') && // html end tag also ends emphasis
731 data[i]!='\n') i++;
732 // avoid overflow (unclosed emph token)
733 if (i==size)
734 {
735 return 0;
736 }
737 //printf("findEmphasisChar: data=[%s] i=%d c=%c\n",data,i,data[i]);
738
739 // not counting escaped chars or characters that are unlikely
740 // to appear as the end of the emphasis char
741 if (ignoreCloseEmphChar(data[i-1],data[i]))
742 {
743 i++;
744 continue;
745 }
746 else
747 {
748 // get length of emphasis token
749 size_t len = 0;
750 while (i+len<size && data[i+len]==c)
751 {
752 len++;
753 }
754
755 if (len>0)
756 {
757 if (len!=c_size || (i+len<size && isIdChar(data[i+len]))) // to prevent touching some_underscore_identifier
758 {
759 i+=len;
760 continue;
761 }
762 AUTO_TRACE_EXIT("result={}",i);
763 return static_cast<int>(i); // found it
764 }
765 }
766
767 // skipping a code span
768 if (data[i]=='`')
769 {
770 int snb=0;
771 while (i<size && data[i]=='`') snb++,i++;
772
773 // find same pattern to end the span
774 int enb=0;
775 while (i<size && enb<snb)
776 {
777 if (data[i]=='`') enb++;
778 if (snb==1 && data[i]=='\'') break; // ` ended by '
779 i++;
780 }
781 }
782 else if (data[i]=='@' || data[i]=='\\')
783 { // skip over blocks that should not be processed
784 QCString endBlockName = isBlockCommand(data.substr(i),i);
785 if (!endBlockName.isEmpty())
786 {
787 i++;
788 size_t l = endBlockName.length();
789 while (i+l<size)
790 {
791 if ((data[i]=='\\' || data[i]=='@') && // command
792 data[i-1]!='\\' && data[i-1]!='@') // not escaped
793 {
794 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
795 {
796 break;
797 }
798 }
799 i++;
800 }
801 }
802 else if (i+1<size && isIdChar(data[i+1])) // @cmd, stop processing, see bug 690385
803 {
804 return 0;
805 }
806 else
807 {
808 i++;
809 }
810 }
811 else if (data[i-1]=='<' && data[i]=='/') // html end tag invalidates emphasis
812 {
813 return 0;
814 }
815 else if (data[i]=='\n') // end * or _ at paragraph boundary
816 {
817 i++;
818 while (i<size && data[i]==' ') i++;
819 if (i>=size || data[i]=='\n')
820 {
821 return 0;
822 } // empty line -> paragraph
823 }
824 else // should not get here!
825 {
826 i++;
827 }
828 }
829 return 0;
830}
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
#define isIdChar(c)
Definition markdown.cpp:77
#define ignoreCloseEmphChar(c, cn)
Definition markdown.cpp:108
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:378

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 3050 of file markdown.cpp.

3051{
3052 AUTO_TRACE("data='{}'",Trace::trunc(data));
3053 // find end of the line
3054 const size_t size = data.size();
3055 size_t nb=0, end=offset+1, j=0;
3056 while (end<=size && (j=isNewline(data.substr(end-1)))==0)
3057 {
3058 // while looking for the end of the line we might encounter a block
3059 // that needs to be passed unprocessed.
3060 if ((data[end-1]=='\\' || data[end-1]=='@') && // command
3061 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@')) // not escaped
3062 )
3063 {
3064 QCString endBlockName = isBlockCommand(data.substr(end-1),end-1);
3065 end++;
3066 if (!endBlockName.isEmpty())
3067 {
3068 size_t l = endBlockName.length();
3069 for (;end+l+1<size;end++) // search for end of block marker
3070 {
3071 if ((data[end]=='\\' || data[end]=='@') &&
3072 data[end-1]!='\\' && data[end-1]!='@'
3073 )
3074 {
3075 if (qstrncmp(&data[end+1],endBlockName.data(),l)==0)
3076 {
3077 // found end marker, skip over this block
3078 //printf("feol.block out={%s}\n",qPrint(QCString(data+i).left(end+l+1-i)));
3079 end = end + l + 2;
3080 break;
3081 }
3082 }
3083 }
3084 }
3085 }
3086 else if (nb==0 && data[end-1]=='<' && size>=6 && end+6<size &&
3087 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@'))
3088 )
3089 {
3090 if (tolower(data[end])=='p' && tolower(data[end+1])=='r' &&
3091 tolower(data[end+2])=='e' && (data[end+3]=='>' || data[end+3]==' ')) // <pre> tag
3092 {
3093 // skip part until including </pre>
3094 end = end + processHtmlTagWrite(data.substr(end-1),end-1,false);
3095 break;
3096 }
3097 else
3098 {
3099 end++;
3100 }
3101 }
3102 else if (nb==0 && data[end-1]=='`')
3103 {
3104 while (end<=size && data[end-1]=='`') end++,nb++;
3105 }
3106 else if (nb>0 && data[end-1]=='`')
3107 {
3108 size_t enb=0;
3109 while (end<=size && data[end-1]=='`') end++,enb++;
3110 if (enb==nb) nb=0;
3111 }
3112 else
3113 {
3114 end++;
3115 }
3116 }
3117 if (j>0) end+=j-1;
3118 AUTO_TRACE_EXIT("offset={} end={}",offset,end);
3119 return end;
3120}
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
size_t isNewline(std::string_view data)
Definition markdown.cpp:215
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 2096 of file markdown.cpp.

2098{
2099 AUTO_TRACE("data='{}' header={} id={} allowAdjustLevel={}",Trace::trunc(data),Trace::trunc(header),id,allowAdjustLevel);
2100 size_t i = 0;
2101 int level = 0, blanks=0;
2102 const size_t size = data.size();
2103
2104 // find start of header text and determine heading level
2105 while (i<size && data[i]==' ') i++;
2106 if (i>=size || data[i]!='#')
2107 {
2108 return 0;
2109 }
2110 while (i<size && data[i]=='#') i++,level++;
2111 if (level>SectionType::MaxLevel) // too many #'s -> no section
2112 {
2113 return 0;
2114 }
2115 while (i<size && data[i]==' ') i++,blanks++;
2116 if (level==1 && blanks==0)
2117 {
2118 return 0; // special case to prevent #someid seen as a header (see bug 671395)
2119 }
2120
2121 // find end of header text
2122 size_t end=i;
2123 while (end<size && data[end]!='\n') end++;
2124 while (end>i && (data[end-1]=='#' || data[end-1]==' ')) end--;
2125
2126 // store result
2127 header = data.substr(i,end-i);
2128 id = extractTitleId(header, level, pIsIdGenerated);
2129 if (!id.isEmpty()) // strip #'s between title and id
2130 {
2131 int idx=static_cast<int>(header.length())-1;
2132 while (idx>=0 && (header.at(idx)=='#' || header.at(idx)==' ')) idx--;
2133 header=header.left(idx+1);
2134 }
2135
2136 if (allowAdjustLevel && level==1 && indentLevel==-1)
2137 {
2138 // in case we find a `# Section` on a markdown page that started with the same level
2139 // header, we no longer need to artificially decrease the paragraph level.
2140 // So both
2141 // -------------------
2142 // # heading 1 <-- here we set g_indentLevel to -1
2143 // # heading 2 <-- here we set g_indentLevel back to 0 such that this will be a @section
2144 // -------------------
2145 // and
2146 // -------------------
2147 // # heading 1 <-- here we set g_indentLevel to -1
2148 // ## heading 2 <-- here we keep g_indentLevel at -1 such that @subsection will be @section
2149 // -------------------
2150 // will convert to
2151 // -------------------
2152 // @page md_page Heading 1
2153 // @section autotoc_md1 Heading 2
2154 // -------------------
2155
2156 indentLevel=0;
2157 }
2158 int res = level+indentLevel;
2159 AUTO_TRACE_EXIT("result={}",res);
2160 return res;
2161}
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 378 of file markdown.cpp.

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

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 1877 of file markdown.cpp.

1878{
1879 AUTO_TRACE("data='{}' allowAdjustLevel",Trace::trunc(data),allowAdjustLevel);
1880 size_t i=0, c=0;
1881 const size_t size = data.size();
1882 while (i<size && data[i]==' ') i++;
1883 if (i==size) return 0;
1884
1885 // test of level 1 header
1886 if (data[i]=='=')
1887 {
1888 while (i<size && data[i]=='=') i++,c++;
1889 while (i<size && data[i]==' ') i++;
1890 int level = (c>1 && (i>=size || data[i]=='\n')) ? 1 : 0;
1891 if (allowAdjustLevel && level==1 && indentLevel==-1)
1892 {
1893 // In case a page starts with a header line we use it as title, promoting it to @page.
1894 // We set g_indentLevel to -1 to promoting the other sections if they have a deeper
1895 // nesting level than the page header, i.e. @section..@subsection becomes @page..@section.
1896 // In case a section at the same level is found (@section..@section) however we need
1897 // to undo this (and the result will be @page..@section).
1898 indentLevel=0;
1899 }
1900 AUTO_TRACE_EXIT("result={}",indentLevel+level);
1901 return indentLevel+level;
1902 }
1903 // test of level 2 header
1904 if (data[i]=='-')
1905 {
1906 while (i<size && data[i]=='-') i++,c++;
1907 while (i<size && data[i]==' ') i++;
1908 return (c>1 && (i>=size || data[i]=='\n')) ? indentLevel+2 : 0;
1909 }
1910 return 0;
1911}

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 447 of file markdown.cpp.

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

3305{
3306 AUTO_TRACE("data='{}' indent={}",Trace::trunc(data),indent);
3307 out.clear();
3308 size_t pi = std::string::npos;
3309 QCString id,link,title;
3310
3311#if 0 // commented out, since starting with a comment block is probably a usage error
3312 // see also http://stackoverflow.com/q/20478611/784672
3313
3314 // special case when the documentation starts with a code block
3315 // since the first line is skipped when looking for a code block later on.
3316 if (end>codeBlockIndent && isCodeBlock(data,0,end,blockIndent))
3317 {
3318 i=writeCodeBlock(out,data,size,blockIndent);
3319 end=i+1;
3320 pi=-1;
3321 }
3322#endif
3323
3324 size_t currentIndent = indent;
3325 size_t listIndent = indent;
3326 bool insideList = false;
3327 bool newBlock = false;
3328 // process each line
3329 size_t i=0;
3330 while (i<data.size())
3331 {
3332 size_t end = findEndOfLine(data,i);
3333 // line is now found at [i..end)
3334
3335 size_t lineIndent=0;
3336 int level = 0;
3337 while (lineIndent<end && data[i+lineIndent]==' ') lineIndent++;
3338 //printf("** lineIndent=%d line=(%s)\n",lineIndent,qPrint(QCString(data+i).left(end-i)));
3339
3340 if (newBlock)
3341 {
3342 //printf("** end of block\n");
3343 if (insideList && lineIndent<currentIndent) // end of list
3344 {
3345 //printf("** end of list\n");
3346 currentIndent = indent;
3347 insideList = false;
3348 }
3349 newBlock = false;
3350 }
3351
3352 if ((listIndent=isListMarker(data.substr(i,end-i)))) // see if we need to increase the indent level
3353 {
3354 if (listIndent<currentIndent+4)
3355 {
3356 //printf("** start of list\n");
3357 insideList = true;
3358 currentIndent = listIndent;
3359 }
3360 }
3361 else if (isEndOfList(data.substr(i,end-i)))
3362 {
3363 //printf("** end of list\n");
3364 insideList = false;
3365 currentIndent = listIndent;
3366 }
3367 else if (isEmptyLine(data.substr(i,end-i)))
3368 {
3369 //printf("** new block\n");
3370 newBlock = true;
3371 }
3372
3373 //printf("indent=%d listIndent=%d blockIndent=%d\n",indent,listIndent,blockIndent);
3374
3375 //printf("findEndOfLine: pi=%d i=%d end=%d\n",pi,i,end);
3376
3377 if (pi!=std::string::npos)
3378 {
3379 size_t blockStart=0, blockEnd=0, blockOffset=0;
3380 QCString lang;
3381 size_t blockIndent = currentIndent;
3382 size_t ref = 0;
3383 //printf("isHeaderLine(%s)=%d\n",QCString(data+i).left(size-i).data(),level);
3384 QCString endBlockName;
3385 if (data[i]=='@' || data[i]=='\\') endBlockName = isBlockCommand(data.substr(i),i);
3386 if (!endBlockName.isEmpty())
3387 {
3388 // handle previous line
3389 if (isLinkRef(data.substr(pi,i-pi),id,link,title))
3390 {
3391 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3392 }
3393 else
3394 {
3395 writeOneLineHeaderOrRuler(data.substr(pi,i-pi));
3396 }
3397 out+=data[i];
3398 i++;
3399 size_t l = endBlockName.length();
3400 while (i+l<data.size())
3401 {
3402 if ((data[i]=='\\' || data[i]=='@') && // command
3403 data[i-1]!='\\' && data[i-1]!='@') // not escaped
3404 {
3405 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
3406 {
3407 out+=data[i];
3408 out+=endBlockName;
3409 i+=l+1;
3410 break;
3411 }
3412 }
3413 out+=data[i];
3414 i++;
3415 }
3416 }
3417 else if ((level=isHeaderline(data.substr(i),TRUE))>0)
3418 {
3419 //printf("Found header at %d-%d\n",i,end);
3420 while (pi<data.size() && data[pi]==' ') pi++;
3421 QCString header = data.substr(pi,i-pi-1);
3422 id = extractTitleId(header, level);
3423 //printf("header='%s' is='%s'\n",qPrint(header),qPrint(id));
3424 if (!header.isEmpty())
3425 {
3426 if (!id.isEmpty())
3427 {
3428 out+=level==1?"@section ":"@subsection ";
3429 out+=id;
3430 out+=" ";
3431 out+=header;
3432 out+="\n\n";
3433 }
3434 else
3435 {
3436 out+=level==1?"<h1>":"<h2>";
3437 out+=header;
3438 out+=level==1?"\n</h1>\n":"\n</h2>\n";
3439 }
3440 }
3441 else
3442 {
3443 out+="\n<hr>\n";
3444 }
3445 pi=std::string::npos;
3446 i=end;
3447 end=i+1;
3448 continue;
3449 }
3450 else if ((ref=isLinkRef(data.substr(pi),id,link,title)))
3451 {
3452 //printf("found link ref: id='%s' link='%s' title='%s'\n",
3453 // qPrint(id),qPrint(link),qPrint(title));
3454 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3455 i=ref+pi;
3456 end=i+1;
3457 }
3458 else if (isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3459 {
3460 //printf("Found FencedCodeBlock lang='%s' start=%d end=%d code={%s}\n",
3461 // qPrint(lang),blockStart,blockEnd,QCString(data+pi+blockStart).left(blockEnd-blockStart).data());
3462 writeFencedCodeBlock(data.substr(pi),lang.view(),blockStart,blockEnd);
3463 i=pi+blockOffset;
3464 pi=std::string::npos;
3465 end=i+1;
3466 continue;
3467 }
3468 else if (isCodeBlock(data.substr(i,end-i),i,blockIndent))
3469 {
3470 // skip previous line (it is empty anyway)
3471 i+=writeCodeBlock(data.substr(i),blockIndent);
3472 pi=std::string::npos;
3473 end=i+1;
3474 continue;
3475 }
3476 else if (isTableBlock(data.substr(pi)))
3477 {
3478 i=pi+writeTableBlock(data.substr(pi));
3479 pi=std::string::npos;
3480 end=i+1;
3481 continue;
3482 }
3483 else
3484 {
3485 writeOneLineHeaderOrRuler(data.substr(pi,i-pi));
3486 }
3487 }
3488 pi=i;
3489 i=end;
3490 }
3491 //printf("last line %d size=%d\n",i,size);
3492 if (pi!=std::string::npos && pi<data.size()) // deal with the last line
3493 {
3494 if (isLinkRef(data.substr(pi),id,link,title))
3495 {
3496 //printf("found link ref: id='%s' link='%s' title='%s'\n",
3497 // qPrint(id),qPrint(link),qPrint(title));
3498 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3499 }
3500 else
3501 {
3502 writeOneLineHeaderOrRuler(data.substr(pi));
3503 }
3504 }
3505
3506 return out;
3507}
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)
const size_t codeBlockIndent
Definition markdown.cpp:209
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 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:182
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(), findEndOfLine(), isBlockCommand(), isCodeBlock(), QCString::isEmpty(), isEmptyLine(), isEndOfList(), isFencedCodeBlock(), isHeaderline(), isLinkRef(), isListMarker(), isTableBlock(), QCString::length(), 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 1647 of file markdown.cpp.

1648{
1649 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1650 const size_t size = data.size();
1651
1652 /* counting the number of backticks in the delimiter */
1653 size_t nb=0, end=0;
1654 while (nb<size && data[nb]=='`')
1655 {
1656 nb++;
1657 }
1658
1659 /* finding the next delimiter with the same amount of backticks */
1660 size_t i = 0;
1661 char pc = '`';
1662 bool markdownStrict = Config_getBool(MARKDOWN_STRICT);
1663 for (end=nb; end<size; end++)
1664 {
1665 //AUTO_TRACE_ADD("c={} nb={} i={} size={}",data[end],nb,i,size);
1666 if (data[end]=='`')
1667 {
1668 i++;
1669 if (nb==1) // `...`
1670 {
1671 if (end+1<size && data[end+1]=='`') // skip over `` inside `...`
1672 {
1673 AUTO_TRACE_ADD("case1.1");
1674 // skip
1675 end++;
1676 i=0;
1677 }
1678 else // normal end of `...`
1679 {
1680 AUTO_TRACE_ADD("case1.2");
1681 break;
1682 }
1683 }
1684 else if (i==nb) // ``...``
1685 {
1686 if (end+1<size && data[end+1]=='`') // do greedy match
1687 {
1688 // skip this quote and use the next one to terminate the sequence, e.g. ``X`Y```
1689 i--;
1690 AUTO_TRACE_ADD("case2.1");
1691 }
1692 else // normal end of ``...``
1693 {
1694 AUTO_TRACE_ADD("case2.2");
1695 break;
1696 }
1697 }
1698 }
1699 else if (data[end]=='\n')
1700 {
1701 // consecutive newlines
1702 if (pc == '\n')
1703 {
1704 AUTO_TRACE_EXIT("new paragraph");
1705 return 0;
1706 }
1707 pc = '\n';
1708 i = 0;
1709 }
1710 else if (!markdownStrict && data[end]=='\'' && nb==1 && (end+1==size || (end+1<size && data[end+1]!='\'' && !isIdChar(data[end+1]))))
1711 { // look for quoted strings like 'some word', but skip strings like `it's cool`
1712 out+="&lsquo;";
1713 out+=data.substr(nb,end-nb);
1714 out+="&rsquo;";
1715 AUTO_TRACE_EXIT("quoted end={}",end+1);
1716 return static_cast<int>(end+1);
1717 }
1718 else if (!markdownStrict && data[end]=='\'' && nb==2 && end+1<size && data[end+1]=='\'')
1719 { // look for '' to match a ``
1720 out+="&ldquo;";
1721 out+=data.substr(nb,end-nb);
1722 out+="&rdquo;";
1723 AUTO_TRACE_EXIT("double quoted end={}",end+1);
1724 return static_cast<int>(end+2);
1725 }
1726 else
1727 {
1728 if (data[end]!=' ') pc = data[end];
1729 i=0;
1730 }
1731 }
1732 if (i < nb && end >= size)
1733 {
1734 AUTO_TRACE_EXIT("no matching delimiter nb={} i={}",nb,i);
1735 if (nb>=3) // found ``` that is not at the start of the line, keep it as-is.
1736 {
1737 out+=data.substr(0,nb);
1738 return nb;
1739 }
1740 return 0; // no matching delimiter
1741 }
1742 while (end<size && data[end]=='`') // do greedy match in case we have more end backticks.
1743 {
1744 end++;
1745 }
1746
1747 //printf("found code span '%s'\n",qPrint(QCString(data+f_begin).left(f_end-f_begin)));
1748
1749 /* real code span */
1750 if (nb+nb < end)
1751 {
1752 QCString codeFragment = data.substr(nb, end-nb-nb);
1753 out+="<tt>";
1754 out+=escapeSpecialChars(codeFragment);
1755 out+="</tt>";
1756 }
1757 AUTO_TRACE_EXIT("result={} nb={}",end,nb);
1758 return static_cast<int>(end);
1759}
#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:246

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

◆ processEmphasis()

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

Definition at line 1123 of file markdown.cpp.

1124{
1125 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1126 const size_t size = data.size();
1127
1128 if (isAllowedEmphStr(data,offset) || // invalid char before * or _
1129 (size>1 && data[0]!=data[1] && !(isIdChar(data[1]) || extraChar(data[1]))) || // invalid char after * or _
1130 (size>2 && data[0]==data[1] && !(isIdChar(data[2]) || extraChar(data[2])))) // invalid char after ** or __
1131 {
1132 AUTO_TRACE_EXIT("invalid surrounding characters");
1133 return 0;
1134 }
1135
1136 char c = data[0];
1137 int ret = 0;
1138 if (size>2 && c!='~' && data[1]!=c) // _bla or *bla
1139 {
1140 // whitespace cannot follow an opening emphasis
1141 if (data[1]==' ' || data[1]=='\n' ||
1142 (ret = processEmphasis1(data.substr(1), c)) == 0)
1143 {
1144 return 0;
1145 }
1146 AUTO_TRACE_EXIT("result={}",ret+1);
1147 return ret+1;
1148 }
1149 if (size>3 && data[1]==c && data[2]!=c) // __bla or **bla
1150 {
1151 if (data[2]==' ' || data[2]=='\n' ||
1152 (ret = processEmphasis2(data.substr(2), c)) == 0)
1153 {
1154 return 0;
1155 }
1156 AUTO_TRACE_EXIT("result={}",ret+2);
1157 return ret+2;
1158 }
1159 if (size>4 && c!='~' && data[1]==c && data[2]==c && data[3]!=c) // ___bla or ***bla
1160 {
1161 if (data[3]==' ' || data[3]=='\n' ||
1162 (ret = processEmphasis3(data.substr(3), c)) == 0)
1163 {
1164 return 0;
1165 }
1166 AUTO_TRACE_EXIT("result={}",ret+3);
1167 return ret+3;
1168 }
1169 return 0;
1170}
#define isAllowedEmphStr(data, offset)
Definition markdown.cpp:102
#define extraChar(c)
Definition markdown.cpp:84
int processEmphasis1(std::string_view data, char c)
process single emphasis
Definition markdown.cpp:833
int processEmphasis3(std::string_view data, char c)
Parsing triple emphasis.
Definition markdown.cpp:899
int processEmphasis2(std::string_view data, char c)
process double emphasis
Definition markdown.cpp:867

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

◆ processEmphasis1()

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

process single emphasis

Definition at line 833 of file markdown.cpp.

834{
835 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
836 size_t i = 0;
837 const size_t size = data.size();
838
839 /* skipping one symbol if coming from emph3 */
840 if (size>1 && data[0]==c && data[1]==c) { i=1; }
841
842 while (i<size)
843 {
844 size_t len = findEmphasisChar(data.substr(i), c, 1);
845 if (len==0) { return 0; }
846 i+=len;
847 if (i>=size) { return 0; }
848
849 if (i+1<size && data[i+1]==c)
850 {
851 i++;
852 continue;
853 }
854 if (data[i]==c && data[i-1]!=' ' && data[i-1]!='\n')
855 {
856 out+="<em>";
857 processInline(data.substr(0,i));
858 out+="</em>";
859 AUTO_TRACE_EXIT("result={}",i+1);
860 return static_cast<int>(i+1);
861 }
862 }
863 return 0;
864}
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:720
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 867 of file markdown.cpp.

868{
869 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
870 size_t i = 0;
871 const size_t size = data.size();
872
873 while (i<size)
874 {
875 size_t len = findEmphasisChar(data.substr(i), c, 2);
876 if (len==0)
877 {
878 return 0;
879 }
880 i += len;
881 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=' ' && data[i-1]!='\n')
882 {
883 if (c == '~') out+="<strike>";
884 else out+="<strong>";
885 processInline(data.substr(0,i));
886 if (c == '~') out+="</strike>";
887 else out+="</strong>";
888 AUTO_TRACE_EXIT("result={}",i+2);
889 return static_cast<int>(i+2);
890 }
891 i++;
892 }
893 return 0;
894}

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 899 of file markdown.cpp.

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

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 1117 of file markdown.cpp.

1118{
1119 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1120 return processHtmlTagWrite(data,offset,true);
1121}

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

◆ 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 1027 of file markdown.cpp.

1028{
1029 AUTO_TRACE("data='{}' offset={} doWrite={}",Trace::trunc(data),offset,doWrite);
1030 if (offset>0 && data.data()[-1]=='\\') { return 0; } // escaped <
1031
1032 const size_t size = data.size();
1033
1034 // find the end of the html tag
1035 size_t i=1;
1036 size_t l=0;
1037 // compute length of the tag name
1038 while (i<size && isIdChar(data[i])) i++,l++;
1039 QCString tagName(data.substr(1,i-1));
1040 if (tagName.lower()=="pre") // found <pre> tag
1041 {
1042 bool insideStr=FALSE;
1043 while (i+6<size)
1044 {
1045 char c=data[i];
1046 if (!insideStr && c=='<') // potential start of html tag
1047 {
1048 if (data[i+1]=='/' &&
1049 tolower(data[i+2])=='p' && tolower(data[i+3])=='r' &&
1050 tolower(data[i+4])=='e' && tolower(data[i+5])=='>')
1051 { // found </pre> tag, copy from start to end of tag
1052 if (doWrite) out+=data.substr(0,i+6);
1053 //printf("found <pre>..</pre> [%d..%d]\n",0,i+6);
1054 AUTO_TRACE_EXIT("result={}",i+6);
1055 return static_cast<int>(i+6);
1056 }
1057 }
1058 else if (insideStr && c=='"')
1059 {
1060 if (data[i-1]!='\\') insideStr=FALSE;
1061 }
1062 else if (c=='"')
1063 {
1064 insideStr=TRUE;
1065 }
1066 i++;
1067 }
1068 }
1069 else // some other html tag
1070 {
1071 if (l>0 && i<size)
1072 {
1073 if (data[i]=='/' && i+1<size && data[i+1]=='>') // <bla/>
1074 {
1075 //printf("Found htmlTag={%s}\n",qPrint(QCString(data).left(i+2)));
1076 if (doWrite) out+=data.substr(0,i+2);
1077 AUTO_TRACE_EXIT("result={}",i+2);
1078 return static_cast<int>(i+2);
1079 }
1080 else if (data[i]=='>') // <bla>
1081 {
1082 //printf("Found htmlTag={%s}\n",qPrint(QCString(data).left(i+1)));
1083 if (doWrite) out+=data.substr(0,i+1);
1084 AUTO_TRACE_EXIT("result={}",i+1);
1085 return static_cast<int>(i+1);
1086 }
1087 else if (data[i]==' ') // <bla attr=...
1088 {
1089 i++;
1090 bool insideAttr=FALSE;
1091 while (i<size)
1092 {
1093 if (!insideAttr && data[i]=='"')
1094 {
1095 insideAttr=TRUE;
1096 }
1097 else if (data[i]=='"' && data[i-1]!='\\')
1098 {
1099 insideAttr=FALSE;
1100 }
1101 else if (!insideAttr && data[i]=='>') // found end of tag
1102 {
1103 //printf("Found htmlTag={%s}\n",qPrint(QCString(data).left(i+1)));
1104 if (doWrite) out+=data.substr(0,i+1);
1105 AUTO_TRACE_EXIT("result={}",i+1);
1106 return static_cast<int>(i+1);
1107 }
1108 i++;
1109 }
1110 }
1111 }
1112 }
1113 AUTO_TRACE_EXIT("not a valid html tag");
1114 return 0;
1115}
#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 1847 of file markdown.cpp.

1848{
1849 AUTO_TRACE("data='{}'",Trace::trunc(data));
1850 size_t i=0;
1851 size_t end=0;
1852 Action_t action;
1853 const size_t size = data.size();
1854 while (i<size)
1855 {
1856 // skip over characters that do not trigger a specific action
1857 while (end<size && ((action=actions[static_cast<uint8_t>(data[end])])==nullptr)) end++;
1858 // and add them to the output
1859 out+=data.substr(i,end-i);
1860 if (end>=size) break;
1861 i=end;
1862 // do the action matching a special character at i
1863 int iend = action(data.substr(i),i);
1864 if (iend<=0) // update end
1865 {
1866 end=i+1-iend;
1867 }
1868 else // skip until end
1869 {
1870 i+=iend;
1871 end=i;
1872 }
1873 }
1874}
std::function< int(std::string_view, size_t)> Action_t
Definition markdown.cpp:180

References 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 1215 of file markdown.cpp.

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

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().

◆ processNmdash()

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

Process ndash and mdashes.

Definition at line 961 of file markdown.cpp.

962{
963 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
964 const size_t size = data.size();
965 // precondition: data[0]=='-'
966 size_t i=1;
967 int count=1;
968 if (i<size && data[i]=='-') // found --
969 {
970 count++,i++;
971 }
972 if (i<size && data[i]=='-') // found ---
973 {
974 count++,i++;
975 }
976 if (i<size && data[i]=='-') // found ----
977 {
978 count++;
979 }
980 if (count>=2 && offset>=2 && literal_at(data.data()-2,"<!"))
981 { AUTO_TRACE_EXIT("result={}",1-count); return 1-count; } // start HTML comment
982 if (count==2 && size > 2 && data[2]=='>')
983 { return 0; } // end HTML comment
984 if (count==3 && size > 3 && data[3]=='>')
985 { return 0; } // end HTML comment
986 if (count==2 && (offset<8 || !literal_at(data.data()-8,"operator"))) // -- => ndash
987 {
988 out+="&ndash;";
989 AUTO_TRACE_EXIT("result=2");
990 return 2;
991 }
992 else if (count==3) // --- => ndash
993 {
994 out+="&mdash;";
995 AUTO_TRACE_EXIT("result=3");
996 return 3;
997 }
998 // not an ndash or mdash
999 return 0;
1000}

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

◆ processQuotations()

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

Definition at line 3145 of file markdown.cpp.

3146{
3147 AUTO_TRACE("data='{}' refIndex='{}'",Trace::trunc(data),refIndent);
3148 out.clear();
3149 size_t i=0,end=0;
3150 size_t pi=std::string::npos;
3151 bool newBlock = false;
3152 bool insideList = false;
3153 size_t currentIndent = refIndent;
3154 size_t listIndent = refIndent;
3155 const size_t size = data.size();
3156 QCString lang;
3157 while (i<size)
3158 {
3159 end = findEndOfLine(data,i);
3160 // line is now found at [i..end)
3161
3162 size_t lineIndent=0;
3163 while (lineIndent<end && data[i+lineIndent]==' ') lineIndent++;
3164 //printf("** lineIndent=%d line=(%s)\n",lineIndent,qPrint(QCString(data+i).left(end-i)));
3165
3166 if (newBlock)
3167 {
3168 //printf("** end of block\n");
3169 if (insideList && lineIndent<currentIndent) // end of list
3170 {
3171 //printf("** end of list\n");
3172 currentIndent = refIndent;
3173 insideList = false;
3174 }
3175 newBlock = false;
3176 }
3177
3178 if ((listIndent=isListMarker(data.substr(i,end-i)))) // see if we need to increase the indent level
3179 {
3180 if (listIndent<currentIndent+4)
3181 {
3182 //printf("** start of list\n");
3183 insideList = true;
3184 currentIndent = listIndent;
3185 }
3186 }
3187 else if (isEndOfList(data.substr(i,end-i)))
3188 {
3189 //printf("** end of list\n");
3190 insideList = false;
3191 currentIndent = listIndent;
3192 }
3193 else if (isEmptyLine(data.substr(i,end-i)))
3194 {
3195 //printf("** new block\n");
3196 newBlock = true;
3197 }
3198 //printf("currentIndent=%d listIndent=%d refIndent=%d\n",currentIndent,listIndent,refIndent);
3199
3200 if (pi!=std::string::npos)
3201 {
3202 size_t blockStart=0, blockEnd=0, blockOffset=0;
3203 if (isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3204 {
3205 auto addSpecialCommand = [&](const QCString &startCmd,const QCString &endCmd)
3206 {
3207 size_t cmdPos = pi+blockStart+1;
3208 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3209 size_t ii = 0;
3210 int nl = 1;
3211 // check for absence of start command, either @start<cmd>, or \\start<cmd>
3212 while (ii<pl.length() && qisspace(pl[ii]))
3213 {
3214 if (pl[ii]=='\n') nl++;
3215 ii++; // skip leading whitespace
3216 }
3217 bool addNewLines = false;
3218 if (ii+startCmd.length()>=pl.length() || // no room for start command
3219 (pl[ii]!='\\' && pl[ii]!='@') || // no @ or \ after whitespace
3220 qstrncmp(pl.data()+ii+1,startCmd.data(),startCmd.length())!=0) // no start command
3221 {
3222 // input: output:
3223 // ----------------------------------------------------
3224 // ```{plantuml} => @startuml
3225 // A->B A->B
3226 // ``` @enduml
3227 // ----------------------------------------------------
3228 pl = "@"+startCmd+"\n" + pl + "@"+endCmd;
3229 addNewLines = false;
3230 }
3231 else // we have a @start... command inside the code block
3232 {
3233 // input: output:
3234 // ----------------------------------------------------
3235 // ```{plantuml} \n
3236 // \n
3237 // @startuml => @startuml
3238 // A->B A->B
3239 // @enduml @enduml
3240 // ``` \n
3241 // ----------------------------------------------------
3242 addNewLines = true;
3243 }
3244 if (addNewLines) for (int j=0;j<nl;j++) out+='\n';
3245 processSpecialCommand(pl.view().substr(ii),ii);
3246 if (addNewLines) out+='\n';
3247 };
3248
3249 if (!Config_getString(PLANTUML_JAR_PATH).isEmpty() && lang=="plantuml")
3250 {
3251 addSpecialCommand("startuml","enduml");
3252 }
3253 else if (Config_getBool(HAVE_DOT) && lang=="dot")
3254 {
3255 addSpecialCommand("dot","enddot");
3256 }
3257 else if (lang=="msc") // msc is built-in
3258 {
3259 addSpecialCommand("msc","endmsc");
3260 }
3261 else // normal code block
3262 {
3263 writeFencedCodeBlock(data.substr(pi),lang.view(),blockStart,blockEnd);
3264 }
3265 i=pi+blockOffset;
3266 pi=std::string::npos;
3267 end=i+1;
3268 continue;
3269 }
3270 else if (isBlockQuote(data.substr(pi,i-pi),currentIndent))
3271 {
3272 i = pi+writeBlockQuote(data.substr(pi));
3273 pi=std::string::npos;
3274 end=i+1;
3275 continue;
3276 }
3277 else
3278 {
3279 //printf("quote out={%s}\n",QCString(data+pi).left(i-pi).data());
3280 out+=data.substr(pi,i-pi);
3281 }
3282 }
3283 pi=i;
3284 i=end;
3285 }
3286 if (pi!=std::string::npos && pi<size) // deal with the last line
3287 {
3288 if (isBlockQuote(data.substr(pi),currentIndent))
3289 {
3290 writeBlockQuote(data.substr(pi));
3291 }
3292 else
3293 {
3294 out+=data.substr(pi);
3295 }
3296 }
3297
3298 //printf("Process quotations\n---- input ----\n%s\n---- output ----\n%s\n------------\n",
3299 // qPrint(s),prv->out.get());
3300
3301 return out;
3302}
#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)

References AUTO_TRACE, Config_getBool, Config_getString, QCString::data(), end(), findEndOfLine(), isBlockQuote(), isEmptyLine(), isEndOfList(), isFencedCodeBlock(), isListMarker(), QCString::length(), out, processSpecialCommand(), qisspace(), qstrncmp(), Trace::trunc(), QCString::view(), 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 1003 of file markdown.cpp.

1004{
1005 AUTO_TRACE("data='{}'",Trace::trunc(data));
1006 const size_t size = data.size();
1007 size_t i=1;
1008 int nl=0;
1009 while (i<size && data[i]!='"' && nl<2)
1010 {
1011 if (data[i]=='\n') nl++;
1012 i++;
1013 }
1014 if (i<size && data[i]=='"' && nl<2)
1015 {
1016 out+=data.substr(0,i+1);
1017 AUTO_TRACE_EXIT("result={}",i+2);
1018 return static_cast<int>(i+1);
1019 }
1020 // not a quoted section
1021 return 0;
1022}

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

◆ processSpecialCommand()

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

Definition at line 1774 of file markdown.cpp.

1775{
1776 AUTO_TRACE("{}",Trace::trunc(data));
1777 const size_t size = data.size();
1778 size_t i=1;
1779 QCString endBlockName = isBlockCommand(data,offset);
1780 if (!endBlockName.isEmpty())
1781 {
1782 AUTO_TRACE_ADD("endBlockName={}",endBlockName);
1783 size_t l = endBlockName.length();
1784 while (i+l<size)
1785 {
1786 if ((data[i]=='\\' || data[i]=='@') && // command
1787 data[i-1]!='\\' && data[i-1]!='@') // not escaped
1788 {
1789 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
1790 {
1791 //printf("found end at %d\n",i);
1792 addStrEscapeUtf8Nbsp(data.substr(0,i+1+l));
1793 AUTO_TRACE_EXIT("result={}",i+1+l);
1794 return static_cast<int>(i+1+l);
1795 }
1796 }
1797 i++;
1798 }
1799 }
1800 size_t endPos = isSpecialCommand(data,offset);
1801 if (endPos>0)
1802 {
1803 out+=data.substr(0,endPos);
1804 return static_cast<int>(endPos);
1805 }
1806 if (size>1 && data[0]=='\\') // escaped characters
1807 {
1808 char c=data[1];
1809 if (c=='[' || c==']' || c=='*' || c=='(' || c==')' || c=='`' || c=='_')
1810 {
1811 out+=data[1];
1812 AUTO_TRACE_EXIT("2");
1813 return 2;
1814 }
1815 else if (c=='\\' || c=='@')
1816 {
1817 out+=data.substr(0,2);
1818 AUTO_TRACE_EXIT("2");
1819 return 2;
1820 }
1821 else if (c=='-' && size>3 && data[2]=='-' && data[3]=='-') // \---
1822 {
1823 out+=data.substr(1,3);
1824 AUTO_TRACE_EXIT("2");
1825 return 4;
1826 }
1827 else if (c=='-' && size>2 && data[2]=='-') // \--
1828 {
1829 out+=data.substr(1,2);
1830 AUTO_TRACE_EXIT("3");
1831 return 3;
1832 }
1833 }
1834 else if (size>1 && data[0]=='@') // escaped characters
1835 {
1836 char c=data[1];
1837 if (c=='\\' || c=='@')
1838 {
1839 out+=data.substr(0,2);
1840 AUTO_TRACE_EXIT("2");
1841 return 2;
1842 }
1843 }
1844 return 0;
1845}
size_t isSpecialCommand(std::string_view data, size_t offset)
Definition markdown.cpp:447
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 processQuotations().

◆ writeBlockQuote()

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

Definition at line 2830 of file markdown.cpp.

2831{
2832 AUTO_TRACE("data='{}'",Trace::trunc(data));
2833 size_t i=0;
2834 int curLevel=0;
2835 size_t end=0;
2836 const size_t size = data.size();
2837 std::string startCmd;
2838 int isGitHubAlert = false;
2839 int isGitHubFirst = false;
2840 while (i<size)
2841 {
2842 // find end of this line
2843 end=i+1;
2844 while (end<=size && data[end-1]!='\n') end++;
2845 size_t j=i;
2846 int level=0;
2847 size_t indent=i;
2848 // compute the quoting level
2849 while (j<end && (data[j]==' ' || data[j]=='>'))
2850 {
2851 if (data[j]=='>') { level++; indent=j+1; }
2852 else if (j>0 && data[j-1]=='>') indent=j+1;
2853 j++;
2854 }
2855 if (indent>0 && j>0 && data[j-1]=='>' &&
2856 !(j==size || data[j]=='\n')) // disqualify last > if not followed by space
2857 {
2858 indent--;
2859 level--;
2860 j--;
2861 }
2862 AUTO_TRACE_ADD("indent={} i={} j={} end={} level={} line={}",indent,i,j,end,level,Trace::trunc(&data[i]));
2863 if (level==0 && j<end-1 && !isListMarker(data.substr(j)) && !isHRuler(data.substr(j)))
2864 {
2865 level = curLevel; // lazy
2866 }
2867 if (level==1)
2868 {
2869 QCString txt = stripWhiteSpace(data.substr(indent,end-indent));
2870 auto it = g_quotationHeaderMap.find(txt.lower().str()); // TODO: in C++20 the std::string can be dropped
2871 if (it != g_quotationHeaderMap.end())
2872 {
2873 isGitHubAlert = true;
2874 isGitHubFirst = true;
2875 startCmd = it->second;
2876 }
2877 }
2878 if (level>curLevel) // quote level increased => add start markers
2879 {
2880 if (level!=1 || !isGitHubAlert) // normal block quote
2881 {
2882 for (int l=curLevel;l<level-1;l++)
2883 {
2884 out+="<blockquote>";
2885 }
2886 out += "<blockquote>&zwj;"; // empty blockquotes are also shown
2887 }
2888 else if (!startCmd.empty()) // GitHub style alert
2889 {
2890 out += startCmd + " ";
2891 }
2892 }
2893 else if (level<curLevel) // quote level decreased => add end markers
2894 {
2895 int decrLevel = curLevel;
2896 if (level==0 && isGitHubAlert)
2897 {
2898 decrLevel--;
2899 }
2900 for (int l=level;l<decrLevel;l++)
2901 {
2902 out += "</blockquote>\\ilinebr ";
2903 }
2904 }
2905 if (level==0)
2906 {
2907 curLevel=0;
2908 break; // end of quote block
2909 }
2910 // copy line without quotation marks
2911 if (curLevel!=0 || !isGitHubAlert)
2912 {
2913 std::string_view txt = data.substr(indent,end-indent);
2914 if (stripWhiteSpace(txt).empty() && !startCmd.empty())
2915 {
2916 if (!isGitHubFirst) out += "<br>";
2917 out += "<br>\n";
2918 }
2919 else
2920 {
2921 out += txt;
2922 }
2923 isGitHubFirst = false;
2924 }
2925 else // GitHub alert section
2926 {
2927 out+= "\n";
2928 }
2929 curLevel=level;
2930 // proceed with next line
2931 i=end;
2932 }
2933 // end of comment within blockquote => add end markers
2934 if (isGitHubAlert) // GitHub alert doesn't have a blockquote
2935 {
2936 curLevel--;
2937 }
2938 for (int l=0;l<curLevel;l++)
2939 {
2940 out+="</blockquote>";
2941 }
2942 AUTO_TRACE_EXIT("i={}",i);
2943 return i;
2944}
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:2240
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 2982 of file markdown.cpp.

2983{
2984 AUTO_TRACE("data='{}' refIndent={}",Trace::trunc(data),refIndent);
2985 const size_t size = data.size();
2986 size_t i=0;
2987 // no need for \ilinebr here as the previous line was empty and was skipped
2988 out+="@iverbatim\n";
2989 int emptyLines=0;
2990 std::string location;
2991 while (i<size)
2992 {
2993 // find end of this line
2994 size_t end=i+1;
2995 while (end<=size && data[end-1]!='\n') end++;
2996 size_t j=i;
2997 size_t indent=0;
2998 while (j<end && data[j]==' ') j++,indent++;
2999 //printf("j=%d end=%d indent=%d refIndent=%d tabSize=%d data={%s}\n",
3000 // j,end,indent,refIndent,Config_getInt(TAB_SIZE),qPrint(QCString(data+i).left(end-i-1)));
3001 if (j==end-1) // empty line
3002 {
3003 emptyLines++;
3004 i=end;
3005 }
3006 else if (indent>=refIndent+codeBlockIndent) // enough indent to continue the code block
3007 {
3008 while (emptyLines>0) // write skipped empty lines
3009 {
3010 // add empty line
3011 out+="\n";
3012 emptyLines--;
3013 }
3014 // add code line minus the indent
3015 size_t offset = i+refIndent+codeBlockIndent;
3016 std::string lineLoc;
3017 if (skipOverFileAndLineCommands(data,codeBlockIndent,offset,lineLoc))
3018 {
3019 location = lineLoc;
3020 }
3021 out+=data.substr(offset,end-offset);
3022 i=end;
3023 }
3024 else // end of code block
3025 {
3026 break;
3027 }
3028 }
3029 out+="@endiverbatim";
3030 if (!location.empty())
3031 {
3032 out+=location;
3033 }
3034 else
3035 {
3036 out+="\\ilinebr ";
3037 }
3038 while (emptyLines>0) // write skipped empty lines
3039 {
3040 // add empty line
3041 out+="\n";
3042 emptyLines--;
3043 }
3044 AUTO_TRACE_EXIT("i={}",i);
3045 return i;
3046}
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 3122 of file markdown.cpp.

3124{
3125 AUTO_TRACE("data='{}' lang={} blockStart={} blockEnd={}",Trace::trunc(data),lang,blockStart,blockEnd);
3126 if (!lang.empty() && lang[0]=='.') lang=lang.substr(1);
3127 const size_t size=data.size();
3128 size_t i=0;
3129 while (i<size && (data[i]==' ' || data[i]=='\t'))
3130 {
3131 out+=data[i++];
3132 blockStart--;
3133 blockEnd--;
3134 }
3135 out+="@icode";
3136 if (!lang.empty())
3137 {
3138 out+="{"+lang+"}";
3139 }
3140 out+=" ";
3141 addStrEscapeUtf8Nbsp(data.substr(blockStart+i,blockEnd-blockStart));
3142 out+="@endicode ";
3143}

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 1172 of file markdown.cpp.

1177{
1178 AUTO_TRACE("fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1179 fmt,inline_img,explicitTitle,Trace::trunc(title),Trace::trunc(content),link,attrs);
1180 QCString attributes = getFilteredImageAttributes(fmt, attrs);
1181 out+="@image";
1182 if (inline_img)
1183 {
1184 out+="{inline}";
1185 }
1186 out+=" ";
1187 out+=fmt;
1188 out+=" ";
1189 out+=link.mid(fd ? 0 : 5);
1190 if (!explicitTitle && !content.isEmpty())
1191 {
1192 out+=" \"";
1193 out+=escapeDoubleQuotes(content);
1194 out+="\"";
1195 }
1196 else if ((content.isEmpty() || explicitTitle) && !title.isEmpty())
1197 {
1198 out+=" \"";
1199 out+=escapeDoubleQuotes(title);
1200 out+="\"";
1201 }
1202 else
1203 {
1204 out+=" ";// so the line break will not be part of the image name
1205 }
1206 if (!attributes.isEmpty())
1207 {
1208 out+=" ";
1209 out+=attributes;
1210 out+=" ";
1211 }
1212 out+="\\ilinebr ";
1213}
static QCString escapeDoubleQuotes(const QCString &s)
Definition markdown.cpp:228
static QCString getFilteredImageAttributes(std::string_view fmt, const QCString &attrs)
parse the image attributes and return attributes for given format
Definition markdown.cpp:331

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 2770 of file markdown.cpp.

2771{
2772 AUTO_TRACE("data='{}'",Trace::trunc(data));
2773 int level=0;
2774 QCString header;
2775 QCString id;
2776 if (isHRuler(data))
2777 {
2778 out+="<hr>\n";
2779 }
2780 else if ((level=isAtxHeader(data,header,id,TRUE)))
2781 {
2782 QCString hTag;
2783 if (!id.isEmpty())
2784 {
2785 switch (level)
2786 {
2787 case SectionType::Section: out+="@section "; break;
2788 case SectionType::Subsection: out+="@subsection "; break;
2789 case SectionType::Subsubsection: out+="@subsubsection "; break;
2790 case SectionType::Paragraph: out+="@paragraph "; break;
2791 case SectionType::Subparagraph: out+="@subparagraph "; break;
2792 case SectionType::Subsubparagraph: out+="@subsubparagraph "; break;
2793 }
2794 out+=id;
2795 out+=" ";
2796 out+=header;
2797 out+="\n";
2798 }
2799 else
2800 {
2801 hTag.sprintf("h%d",level);
2802 out+="<"+hTag+">";
2803 out+=header;
2804 out+="</"+hTag+">\n";
2805 }
2806 }
2807 else if (data.size()>0) // nothing interesting -> just output the line
2808 {
2809 size_t tmpSize = data.size();
2810 if (data[data.size()-1] == '\n') tmpSize--;
2811 out+=data.substr(0,tmpSize);
2812
2813 if (hasLineBreak(data))
2814 {
2815 out+="\\ilinebr<br>";
2816 }
2817 if (tmpSize != data.size()) out+='\n';
2818 }
2819}
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 2551 of file markdown.cpp.

2552{
2553 AUTO_TRACE("data='{}'",Trace::trunc(data));
2554 const size_t size = data.size();
2555
2556 size_t columns=0, start=0, end=0;
2557 size_t i = findTableColumns(data,start,end,columns);
2558 size_t headerStart = start;
2559 size_t headerEnd = end;
2560
2561 // read cell alignments
2562 size_t cc = 0;
2563 size_t ret = findTableColumns(data.substr(i),start,end,cc);
2564 size_t k=0;
2565 std::vector<int> columnAlignment(columns);
2566
2567 bool leftMarker=false, rightMarker=false, startFound=false;
2568 size_t j=start+i;
2569 while (j<=end+i)
2570 {
2571 if (!startFound)
2572 {
2573 if (data[j]==':') { leftMarker=TRUE; startFound=TRUE; }
2574 if (data[j]=='-') startFound=TRUE;
2575 //printf(" data[%d]=%c startFound=%d\n",j,data[j],startFound);
2576 }
2577 if (data[j]=='-') rightMarker=FALSE;
2578 else if (data[j]==':') rightMarker=TRUE;
2579 if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2580 {
2581 if (k<columns)
2582 {
2583 columnAlignment[k] = markersToAlignment(leftMarker,rightMarker);
2584 //printf("column[%d] alignment=%d\n",k,columnAlignment[k]);
2585 leftMarker=FALSE;
2586 rightMarker=FALSE;
2587 startFound=FALSE;
2588 }
2589 k++;
2590 }
2591 j++;
2592 }
2593 if (k<columns)
2594 {
2595 columnAlignment[k] = markersToAlignment(leftMarker,rightMarker);
2596 //printf("column[%d] alignment=%d\n",k,columnAlignment[k]);
2597 }
2598 // proceed to next line
2599 i+=ret;
2600
2601 // Store the table cell information by row then column. This
2602 // allows us to handle row spanning.
2603 std::vector<std::vector<TableCell> > tableContents;
2604
2605 size_t m = headerStart;
2606 std::vector<TableCell> headerContents(columns);
2607 for (k=0;k<columns;k++)
2608 {
2609 while (m<=headerEnd && (data[m]!='|' || (m>0 && data[m-1]=='\\')))
2610 {
2611 headerContents[k].cellText += data[m++];
2612 }
2613 m++;
2614 // do the column span test before stripping white space
2615 // || is spanning columns, | | is not
2616 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2617 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2618 }
2619 tableContents.push_back(headerContents);
2620
2621 // write table cells
2622 while (i<size)
2623 {
2624 ret = findTableColumns(data.substr(i),start,end,cc);
2625 if (cc!=columns) break; // end of table
2626
2627 j=start+i;
2628 k=0;
2629 std::vector<TableCell> rowContents(columns);
2630 while (j<=end+i)
2631 {
2632 if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2633 {
2634 // do the column span test before stripping white space
2635 // || is spanning columns, | | is not
2636 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2637 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2638 k++;
2639 } // if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2640 else
2641 {
2642 rowContents[k].cellText += data[j];
2643 } // else { if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\'))) }
2644 j++;
2645 } // while (j<=end+i)
2646 // do the column span test before stripping white space
2647 // || is spanning columns, | | is not
2648 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2649 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2650 tableContents.push_back(rowContents);
2651
2652 // proceed to next line
2653 i+=ret;
2654 }
2655
2656 out+="<table class=\"markdownTable\">";
2657 QCString cellTag("th"), cellClass("class=\"markdownTableHead");
2658 for (size_t row = 0; row < tableContents.size(); row++)
2659 {
2660 if (row)
2661 {
2662 if (row % 2)
2663 {
2664 out+="\n<tr class=\"markdownTableRowOdd\">";
2665 }
2666 else
2667 {
2668 out+="\n<tr class=\"markdownTableRowEven\">";
2669 }
2670 }
2671 else
2672 {
2673 out+="\n <tr class=\"markdownTableHead\">";
2674 }
2675 for (size_t c = 0; c < columns; c++)
2676 {
2677 // save the cell text for use after column span computation
2678 QCString cellText(tableContents[row][c].cellText);
2679
2680 // Row span handling. Spanning rows will contain a caret ('^').
2681 // If the current cell contains just a caret, this is part of an
2682 // earlier row's span and the cell should not be added to the
2683 // output.
2684 if (tableContents[row][c].cellText == "^")
2685 {
2686 continue;
2687 }
2688 if (tableContents[row][c].colSpan)
2689 {
2690 int cr = static_cast<int>(c);
2691 while ( cr >= 0 && tableContents[row][cr].colSpan)
2692 {
2693 cr--;
2694 };
2695 if (cr >= 0 && tableContents[row][cr].cellText == "^") continue;
2696 }
2697 size_t rowSpan = 1, spanRow = row+1;
2698 while ((spanRow < tableContents.size()) &&
2699 (tableContents[spanRow][c].cellText == "^"))
2700 {
2701 spanRow++;
2702 rowSpan++;
2703 }
2704
2705 out+=" <" + cellTag + " " + cellClass;
2706 // use appropriate alignment style
2707 switch (columnAlignment[c])
2708 {
2709 case AlignLeft: out+="Left\""; break;
2710 case AlignRight: out+="Right\""; break;
2711 case AlignCenter: out+="Center\""; break;
2712 case AlignNone: out+="None\""; break;
2713 }
2714
2715 if (rowSpan > 1)
2716 {
2717 QCString spanStr;
2718 spanStr.setNum(rowSpan);
2719 out+=" rowspan=\"" + spanStr + "\"";
2720 }
2721 // Column span handling, assumes that column spans will have
2722 // empty strings, which would indicate the sequence "||", used
2723 // to signify spanning columns.
2724 size_t colSpan = 1;
2725 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2726 {
2727 c++;
2728 colSpan++;
2729 }
2730 if (colSpan > 1)
2731 {
2732 QCString spanStr;
2733 spanStr.setNum(colSpan);
2734 out+=" colspan=\"" + spanStr + "\"";
2735 }
2736 // need at least one space on either side of the cell text in
2737 // order for doxygen to do other formatting
2738 out+="> " + cellText + " \\ilinebr </" + cellTag + ">";
2739 }
2740 cellTag = "td";
2741 cellClass = "class=\"markdownTableBody";
2742 out+=" </tr>";
2743 }
2744 out+="</table>\n";
2745
2746 AUTO_TRACE_EXIT("i={}",i);
2747 return i;
2748}
QCString & setNum(short n)
Definition qcstring.h:459
static 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:310
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.
@ AlignLeft
Definition markdown.cpp:202
@ AlignNone
Definition markdown.cpp:202
@ AlignRight
Definition markdown.cpp:202
@ AlignCenter
Definition markdown.cpp:202

References AlignCenter, AlignLeft, AlignNone, AlignRight, AUTO_TRACE, AUTO_TRACE_EXIT, end(), FALSE, findTableColumns(), markersToAlignment(), out, QCString::setNum(), TRUE, and Trace::trunc().

Referenced by processBlocks().

Member Data Documentation

◆ actions

std::array<Action_t,256> Markdown::Private::actions

Definition at line 187 of file markdown.cpp.

Referenced by processInline().

◆ fileName

QCString Markdown::Private::fileName

Definition at line 183 of file markdown.cpp.

Referenced by extractTitleId(), and processLink().

◆ indentLevel

int Markdown::Private::indentLevel =0

Definition at line 185 of file markdown.cpp.

Referenced by isAtxHeader(), and isHeaderline().

◆ lineNr

int Markdown::Private::lineNr = 0

Definition at line 184 of file markdown.cpp.

Referenced by extractTitleId().

◆ linkRefs

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

Definition at line 182 of file markdown.cpp.

Referenced by processBlocks(), and processLink().

◆ out


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