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

Constructor & Destructor Documentation

◆ Private()

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

Definition at line 115 of file markdown.cpp.

116 : fileName(fn), lineNr(line), indentLevel(indent)
117 {
118 // setup callback table for special characters
119 actions[static_cast<unsigned int>('_')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
120 actions[static_cast<unsigned int>('*')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
121 actions[static_cast<unsigned int>('~')] = [this](std::string_view data,size_t offset) { return processEmphasis (data,offset); };
122 actions[static_cast<unsigned int>('`')] = [this](std::string_view data,size_t offset) { return processCodeSpan (data,offset); };
123 actions[static_cast<unsigned int>('\\')]= [this](std::string_view data,size_t offset) { return processSpecialCommand(data,offset); };
124 actions[static_cast<unsigned int>('@')] = [this](std::string_view data,size_t offset) { return processSpecialCommand(data,offset); };
125 actions[static_cast<unsigned int>('[')] = [this](std::string_view data,size_t offset) { return processLink (data,offset); };
126 actions[static_cast<unsigned int>('!')] = [this](std::string_view data,size_t offset) { return processLink (data,offset); };
127 actions[static_cast<unsigned int>('<')] = [this](std::string_view data,size_t offset) { return processHtmlTag (data,offset); };
128 actions[static_cast<unsigned int>('-')] = [this](std::string_view data,size_t offset) { return processNmdash (data,offset); };
129 actions[static_cast<unsigned int>('"')] = [this](std::string_view data,size_t offset) { return processQuoted (data,offset); };
130 }
int processQuoted(std::string_view data, size_t offset)
Process quoted section "...", can contain one embedded newline.
Definition markdown.cpp:985
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:943
std::array< Action_t, 256 > actions
Definition markdown.cpp:179

Member Function Documentation

◆ addStrEscapeUtf8Nbsp()

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

Definition at line 1701 of file markdown.cpp.

1702{
1703 AUTO_TRACE("{}",Trace::trunc(data));
1704 if (Portable::strnstr(data.data(),g_doxy_nbsp,data.size())==nullptr) // no escape needed -> fast
1705 {
1706 out+=data;
1707 }
1708 else // escape needed -> slow
1709 {
1710 out+=substitute(QCString(data),g_doxy_nbsp,g_utf8_nbsp);
1711 }
1712}
#define AUTO_TRACE(...)
Definition docnode.cpp:46
const char * g_doxy_nbsp
Definition markdown.cpp:200
const char * g_utf8_nbsp
Definition markdown.cpp:199
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
Definition portable.cpp:617
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:477

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

2005{
2006 AUTO_TRACE("title={} level={}",Trace::trunc(title),level);
2007 // match e.g. '{#id-b11} ' and capture 'id-b11'
2008 static const reg::Ex r2(R"({#(\a[\w-]*)}\s*$)");
2009 reg::Match match;
2010 std::string ti = title.str();
2011 if (reg::search(ti,match,r2))
2012 {
2013 std::string id = match[1].str();
2014 title = title.left(match.position());
2015 if (AnchorGenerator::instance().reserve(id)>0)
2016 {
2017 warn(fileName, lineNr, "An automatically generated id already has the name '{}'!", id);
2018 }
2019 //printf("found match id='%s' title=%s\n",id.c_str(),qPrint(title));
2020 AUTO_TRACE_EXIT("id={}",id);
2021 return id;
2022 }
2023 if (((level>0) && (level<=Config_getInt(TOC_INCLUDE_HEADINGS))) || (Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2024 {
2025 QCString id = AnchorGenerator::instance().generate(ti);
2026 if (pIsIdGenerated) *pIsIdGenerated=true;
2027 //printf("auto-generated id='%s' title='%s'\n",qPrint(id),qPrint(title));
2028 AUTO_TRACE_EXIT("id={}",id);
2029 return id;
2030 }
2031 //printf("no id found in title '%s'\n",qPrint(title));
2032 return "";
2033}
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:537
QCString left(size_t len) const
Definition qcstring.h:214
#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 702 of file markdown.cpp.

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

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

2991{
2992 AUTO_TRACE("data='{}'",Trace::trunc(data));
2993 // find end of the line
2994 const size_t size = data.size();
2995 size_t nb=0, end=offset+1, j=0;
2996 while (end<=size && (j=isNewline(data.data()+end-1))==0)
2997 {
2998 // while looking for the end of the line we might encounter a block
2999 // that needs to be passed unprocessed.
3000 if ((data[end-1]=='\\' || data[end-1]=='@') && // command
3001 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@')) // not escaped
3002 )
3003 {
3004 QCString endBlockName = isBlockCommand(data.substr(end-1),end-1);
3005 end++;
3006 if (!endBlockName.isEmpty())
3007 {
3008 size_t l = endBlockName.length();
3009 for (;end+l+1<size;end++) // search for end of block marker
3010 {
3011 if ((data[end]=='\\' || data[end]=='@') &&
3012 data[end-1]!='\\' && data[end-1]!='@'
3013 )
3014 {
3015 if (qstrncmp(&data[end+1],endBlockName.data(),l)==0)
3016 {
3017 // found end marker, skip over this block
3018 //printf("feol.block out={%s}\n",qPrint(QCString(data+i).left(end+l+1-i)));
3019 end = end + l + 2;
3020 break;
3021 }
3022 }
3023 }
3024 }
3025 }
3026 else if (nb==0 && data[end-1]=='<' && size>=6 && end+6<size &&
3027 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@'))
3028 )
3029 {
3030 if (tolower(data[end])=='p' && tolower(data[end+1])=='r' &&
3031 tolower(data[end+2])=='e' && (data[end+3]=='>' || data[end+3]==' ')) // <pre> tag
3032 {
3033 // skip part until including </pre>
3034 end = end + processHtmlTagWrite(data.substr(end-1),end-1,false);
3035 break;
3036 }
3037 else
3038 {
3039 end++;
3040 }
3041 }
3042 else if (nb==0 && data[end-1]=='`')
3043 {
3044 while (end<=size && data[end-1]=='`') end++,nb++;
3045 }
3046 else if (nb>0 && data[end-1]=='`')
3047 {
3048 size_t enb=0;
3049 while (end<=size && data[end-1]=='`') end++,enb++;
3050 if (enb==nb) nb=0;
3051 }
3052 else
3053 {
3054 end++;
3055 }
3056 }
3057 if (j>0) end+=j-1;
3058 AUTO_TRACE_EXIT("offset={} end={}",offset,end);
3059 return end;
3060}
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
size_t isNewline(std::string_view data)
Definition markdown.cpp:207
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 2036 of file markdown.cpp.

2038{
2039 AUTO_TRACE("data='{}' header={} id={} allowAdjustLevel={}",Trace::trunc(data),Trace::trunc(header),id,allowAdjustLevel);
2040 size_t i = 0;
2041 int level = 0, blanks=0;
2042 const size_t size = data.size();
2043
2044 // find start of header text and determine heading level
2045 while (i<size && data[i]==' ') i++;
2046 if (i>=size || data[i]!='#')
2047 {
2048 return 0;
2049 }
2050 while (i<size && data[i]=='#') i++,level++;
2051 if (level>SectionType::MaxLevel) // too many #'s -> no section
2052 {
2053 return 0;
2054 }
2055 while (i<size && data[i]==' ') i++,blanks++;
2056 if (level==1 && blanks==0)
2057 {
2058 return 0; // special case to prevent #someid seen as a header (see bug 671395)
2059 }
2060
2061 // find end of header text
2062 size_t end=i;
2063 while (end<size && data[end]!='\n') end++;
2064 while (end>i && (data[end-1]=='#' || data[end-1]==' ')) end--;
2065
2066 // store result
2067 header = data.substr(i,end-i);
2068 id = extractTitleId(header, level, pIsIdGenerated);
2069 if (!id.isEmpty()) // strip #'s between title and id
2070 {
2071 int idx=static_cast<int>(header.length())-1;
2072 while (idx>=0 && (header.at(idx)=='#' || header.at(idx)==' ')) idx--;
2073 header=header.left(idx+1);
2074 }
2075
2076 if (allowAdjustLevel && level==1 && indentLevel==-1)
2077 {
2078 // in case we find a `# Section` on a markdown page that started with the same level
2079 // header, we no longer need to artificially decrease the paragraph level.
2080 // So both
2081 // -------------------
2082 // # heading 1 <-- here we set g_indentLevel to -1
2083 // # heading 2 <-- here we set g_indentLevel back to 0 such that this will be a @section
2084 // -------------------
2085 // and
2086 // -------------------
2087 // # heading 1 <-- here we set g_indentLevel to -1
2088 // ## heading 2 <-- here we keep g_indentLevel at -1 such that @subsection will be @section
2089 // -------------------
2090 // will convert to
2091 // -------------------
2092 // @page md_page Heading 1
2093 // @section autotoc_md1 Heading 2
2094 // -------------------
2095
2096 indentLevel=0;
2097 }
2098 int res = level+indentLevel;
2099 AUTO_TRACE_EXIT("result={}",res);
2100 return res;
2101}
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:578
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 360 of file markdown.cpp.

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

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

1818{
1819 AUTO_TRACE("data='{}' allowAdjustLevel",Trace::trunc(data),allowAdjustLevel);
1820 size_t i=0, c=0;
1821 const size_t size = data.size();
1822 while (i<size && data[i]==' ') i++;
1823 if (i==size) return 0;
1824
1825 // test of level 1 header
1826 if (data[i]=='=')
1827 {
1828 while (i<size && data[i]=='=') i++,c++;
1829 while (i<size && data[i]==' ') i++;
1830 int level = (c>1 && (i>=size || data[i]=='\n')) ? 1 : 0;
1831 if (allowAdjustLevel && level==1 && indentLevel==-1)
1832 {
1833 // In case a page starts with a header line we use it as title, promoting it to @page.
1834 // We set g_indentLevel to -1 to promoting the other sections if they have a deeper
1835 // nesting level than the page header, i.e. @section..@subsection becomes @page..@section.
1836 // In case a section at the same level is found (@section..@section) however we need
1837 // to undo this (and the result will be @page..@section).
1838 indentLevel=0;
1839 }
1840 AUTO_TRACE_EXIT("result={}",indentLevel+level);
1841 return indentLevel+level;
1842 }
1843 // test of level 2 header
1844 if (data[i]=='-')
1845 {
1846 while (i<size && data[i]=='-') i++,c++;
1847 while (i<size && data[i]==' ') i++;
1848 return (c>1 && (i>=size || data[i]=='\n')) ? indentLevel+2 : 0;
1849 }
1850 return 0;
1851}

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

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

3245{
3246 AUTO_TRACE("data='{}' indent={}",Trace::trunc(data),indent);
3247 out.clear();
3248 size_t pi = std::string::npos;
3249 QCString id,link,title;
3250
3251#if 0 // commented out, since starting with a comment block is probably a usage error
3252 // see also http://stackoverflow.com/q/20478611/784672
3253
3254 // special case when the documentation starts with a code block
3255 // since the first line is skipped when looking for a code block later on.
3256 if (end>codeBlockIndent && isCodeBlock(data,0,end,blockIndent))
3257 {
3258 i=writeCodeBlock(out,data,size,blockIndent);
3259 end=i+1;
3260 pi=-1;
3261 }
3262#endif
3263
3264 size_t currentIndent = indent;
3265 size_t listIndent = indent;
3266 bool insideList = false;
3267 bool newBlock = false;
3268 // process each line
3269 size_t i=0;
3270 while (i<data.size())
3271 {
3272 size_t end = findEndOfLine(data,i);
3273 // line is now found at [i..end)
3274
3275 size_t lineIndent=0;
3276 int level = 0;
3277 while (lineIndent<end && data[i+lineIndent]==' ') lineIndent++;
3278 //printf("** lineIndent=%d line=(%s)\n",lineIndent,qPrint(QCString(data+i).left(end-i)));
3279
3280 if (newBlock)
3281 {
3282 //printf("** end of block\n");
3283 if (insideList && lineIndent<currentIndent) // end of list
3284 {
3285 //printf("** end of list\n");
3286 currentIndent = indent;
3287 insideList = false;
3288 }
3289 newBlock = false;
3290 }
3291
3292 if ((listIndent=isListMarker(data.substr(i,end-i)))) // see if we need to increase the indent level
3293 {
3294 if (listIndent<currentIndent+4)
3295 {
3296 //printf("** start of list\n");
3297 insideList = true;
3298 currentIndent = listIndent;
3299 }
3300 }
3301 else if (isEndOfList(data.substr(i,end-i)))
3302 {
3303 //printf("** end of list\n");
3304 insideList = false;
3305 currentIndent = listIndent;
3306 }
3307 else if (isEmptyLine(data.substr(i,end-i)))
3308 {
3309 //printf("** new block\n");
3310 newBlock = true;
3311 }
3312
3313 //printf("indent=%d listIndent=%d blockIndent=%d\n",indent,listIndent,blockIndent);
3314
3315 //printf("findEndOfLine: pi=%d i=%d end=%d\n",pi,i,end);
3316
3317 if (pi!=std::string::npos)
3318 {
3319 size_t blockStart=0, blockEnd=0, blockOffset=0;
3320 QCString lang;
3321 size_t blockIndent = currentIndent;
3322 size_t ref = 0;
3323 //printf("isHeaderLine(%s)=%d\n",QCString(data+i).left(size-i).data(),level);
3324 QCString endBlockName;
3325 if (data[i]=='@' || data[i]=='\\') endBlockName = isBlockCommand(data.substr(i),i);
3326 if (!endBlockName.isEmpty())
3327 {
3328 // handle previous line
3329 if (isLinkRef(data.substr(pi,i-pi),id,link,title))
3330 {
3331 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3332 }
3333 else
3334 {
3335 writeOneLineHeaderOrRuler(data.substr(pi,i-pi));
3336 }
3337 out+=data[i];
3338 i++;
3339 size_t l = endBlockName.length();
3340 while (i+l<data.size())
3341 {
3342 if ((data[i]=='\\' || data[i]=='@') && // command
3343 data[i-1]!='\\' && data[i-1]!='@') // not escaped
3344 {
3345 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
3346 {
3347 out+=data[i];
3348 out+=endBlockName;
3349 i+=l+1;
3350 break;
3351 }
3352 }
3353 out+=data[i];
3354 i++;
3355 }
3356 }
3357 else if ((level=isHeaderline(data.substr(i),TRUE))>0)
3358 {
3359 //printf("Found header at %d-%d\n",i,end);
3360 while (pi<data.size() && data[pi]==' ') pi++;
3361 QCString header = data.substr(pi,i-pi-1);
3362 id = extractTitleId(header, level);
3363 //printf("header='%s' is='%s'\n",qPrint(header),qPrint(id));
3364 if (!header.isEmpty())
3365 {
3366 if (!id.isEmpty())
3367 {
3368 out+=level==1?"@section ":"@subsection ";
3369 out+=id;
3370 out+=" ";
3371 out+=header;
3372 out+="\n\n";
3373 }
3374 else
3375 {
3376 out+=level==1?"<h1>":"<h2>";
3377 out+=header;
3378 out+=level==1?"\n</h1>\n":"\n</h2>\n";
3379 }
3380 }
3381 else
3382 {
3383 out+="\n<hr>\n";
3384 }
3385 pi=std::string::npos;
3386 i=end;
3387 end=i+1;
3388 continue;
3389 }
3390 else if ((ref=isLinkRef(data.substr(pi),id,link,title)))
3391 {
3392 //printf("found link ref: id='%s' link='%s' title='%s'\n",
3393 // qPrint(id),qPrint(link),qPrint(title));
3394 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3395 i=ref+pi;
3396 end=i+1;
3397 }
3398 else if (isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3399 {
3400 //printf("Found FencedCodeBlock lang='%s' start=%d end=%d code={%s}\n",
3401 // qPrint(lang),blockStart,blockEnd,QCString(data+pi+blockStart).left(blockEnd-blockStart).data());
3402 writeFencedCodeBlock(data.substr(pi),lang.view(),blockStart,blockEnd);
3403 i=pi+blockOffset;
3404 pi=std::string::npos;
3405 end=i+1;
3406 continue;
3407 }
3408 else if (isCodeBlock(data.substr(i,end-i),i,blockIndent))
3409 {
3410 // skip previous line (it is empty anyway)
3411 i+=writeCodeBlock(data.substr(i),blockIndent);
3412 pi=std::string::npos;
3413 end=i+1;
3414 continue;
3415 }
3416 else if (isTableBlock(data.substr(pi)))
3417 {
3418 i=pi+writeTableBlock(data.substr(pi));
3419 pi=std::string::npos;
3420 end=i+1;
3421 continue;
3422 }
3423 else
3424 {
3425 writeOneLineHeaderOrRuler(data.substr(pi,i-pi));
3426 }
3427 }
3428 pi=i;
3429 i=end;
3430 }
3431 //printf("last line %d size=%d\n",i,size);
3432 if (pi!=std::string::npos && pi<data.size()) // deal with the last line
3433 {
3434 if (isLinkRef(data.substr(pi),id,link,title))
3435 {
3436 //printf("found link ref: id='%s' link='%s' title='%s'\n",
3437 // qPrint(id),qPrint(link),qPrint(title));
3438 linkRefs.emplace(id.lower().str(),LinkRef(link,title));
3439 }
3440 else
3441 {
3442 writeOneLineHeaderOrRuler(data.substr(pi));
3443 }
3444 }
3445
3446 return out;
3447}
std::string_view view() const
Definition qcstring.h:161
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:201
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:174
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 1629 of file markdown.cpp.

1630{
1631 AUTO_TRACE("data='{}'",Trace::trunc(data));
1632 const size_t size = data.size();
1633
1634 /* counting the number of backticks in the delimiter */
1635 size_t nb=0, end=0;
1636 while (nb<size && data[nb]=='`')
1637 {
1638 nb++;
1639 }
1640
1641 /* finding the next delimiter */
1642 size_t i = 0;
1643 char pc = '`';
1644 for (end=nb; end<size && i<nb; end++)
1645 {
1646 if (data[end]=='`')
1647 {
1648 i++;
1649 }
1650 else if (data[end]=='\n')
1651 {
1652 // consecutive newlines
1653 if (pc == '\n') return 0;
1654 pc = '\n';
1655 i = 0;
1656 }
1657 else if (data[end]=='\'' && nb==1 && (end==size-1 || (end+1<size && !isIdChar(data[end+1]))))
1658 { // look for quoted strings like 'some word', but skip strings like `it's cool`
1659 out+="&lsquo;";
1660 out+=data.substr(nb,end-nb);
1661 out+="&rsquo;";
1662 return static_cast<int>(end+1);
1663 }
1664 else
1665 {
1666 if (data[end]!=' ') pc = data[end];
1667 i=0;
1668 }
1669 }
1670 if (i < nb && end >= size)
1671 {
1672 return 0; // no matching delimiter
1673 }
1674
1675 // trimming outside whitespaces
1676 size_t f_begin = nb;
1677 while (f_begin < end && data[f_begin]==' ')
1678 {
1679 f_begin++;
1680 }
1681 size_t f_end = end - nb;
1682 while (f_end > nb && data[f_end-1]==' ')
1683 {
1684 f_end--;
1685 }
1686
1687 //printf("found code span '%s'\n",qPrint(QCString(data+f_begin).left(f_end-f_begin)));
1688
1689 /* real code span */
1690 if (f_begin < f_end)
1691 {
1692 QCString codeFragment = data.substr(f_begin, f_end-f_begin);
1693 out+="<tt>";
1694 out+=escapeSpecialChars(codeFragment);
1695 out+="</tt>";
1696 }
1697 AUTO_TRACE_EXIT("result={}",end);
1698 return static_cast<int>(end);
1699}
static QCString escapeSpecialChars(const QCString &s)
Definition markdown.cpp:238

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

◆ processEmphasis()

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

Definition at line 1105 of file markdown.cpp.

1106{
1107 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1108 const size_t size = data.size();
1109
1110 if ((offset>0 && !isOpenEmphChar(data.data()[-1])) || // invalid char before * or _
1111 (size>1 && data[0]!=data[1] && !(isIdChar(data[1]) || extraChar(data[1]))) || // invalid char after * or _
1112 (size>2 && data[0]==data[1] && !(isIdChar(data[2]) || extraChar(data[2])))) // invalid char after ** or __
1113 {
1114 AUTO_TRACE_EXIT("invalid surrounding characters");
1115 return 0;
1116 }
1117
1118 char c = data[0];
1119 int ret = 0;
1120 if (size>2 && c!='~' && data[1]!=c) // _bla or *bla
1121 {
1122 // whitespace cannot follow an opening emphasis
1123 if (data[1]==' ' || data[1]=='\n' ||
1124 (ret = processEmphasis1(data.substr(1), c)) == 0)
1125 {
1126 return 0;
1127 }
1128 AUTO_TRACE_EXIT("result={}",ret+1);
1129 return ret+1;
1130 }
1131 if (size>3 && data[1]==c && data[2]!=c) // __bla or **bla
1132 {
1133 if (data[2]==' ' || data[2]=='\n' ||
1134 (ret = processEmphasis2(data.substr(2), c)) == 0)
1135 {
1136 return 0;
1137 }
1138 AUTO_TRACE_EXIT("result={}",ret+2);
1139 return ret+2;
1140 }
1141 if (size>4 && c!='~' && data[1]==c && data[2]==c && data[3]!=c) // ___bla or ***bla
1142 {
1143 if (data[3]==' ' || data[3]=='\n' ||
1144 (ret = processEmphasis3(data.substr(3), c)) == 0)
1145 {
1146 return 0;
1147 }
1148 AUTO_TRACE_EXIT("result={}",ret+3);
1149 return ret+3;
1150 }
1151 return 0;
1152}
#define isOpenEmphChar(c)
Definition markdown.cpp:93
#define extraChar(c)
Definition markdown.cpp:84
int processEmphasis1(std::string_view data, char c)
process single emphasis
Definition markdown.cpp:815
int processEmphasis3(std::string_view data, char c)
Parsing triple emphasis.
Definition markdown.cpp:881
int processEmphasis2(std::string_view data, char c)
process double emphasis
Definition markdown.cpp:849

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

◆ processEmphasis1()

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

process single emphasis

Definition at line 815 of file markdown.cpp.

816{
817 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
818 size_t i = 0;
819 const size_t size = data.size();
820
821 /* skipping one symbol if coming from emph3 */
822 if (size>1 && data[0]==c && data[1]==c) { i=1; }
823
824 while (i<size)
825 {
826 size_t len = findEmphasisChar(data.substr(i), c, 1);
827 if (len==0) { return 0; }
828 i+=len;
829 if (i>=size) { return 0; }
830
831 if (i+1<size && data[i+1]==c)
832 {
833 i++;
834 continue;
835 }
836 if (data[i]==c && data[i-1]!=' ' && data[i-1]!='\n')
837 {
838 out+="<em>";
839 processInline(data.substr(0,i));
840 out+="</em>";
841 AUTO_TRACE_EXIT("result={}",i+1);
842 return static_cast<int>(i+1);
843 }
844 }
845 return 0;
846}
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:702
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 849 of file markdown.cpp.

850{
851 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
852 size_t i = 0;
853 const size_t size = data.size();
854
855 while (i<size)
856 {
857 size_t len = findEmphasisChar(data.substr(i), c, 2);
858 if (len==0)
859 {
860 return 0;
861 }
862 i += len;
863 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=' ' && data[i-1]!='\n')
864 {
865 if (c == '~') out+="<strike>";
866 else out+="<strong>";
867 processInline(data.substr(0,i));
868 if (c == '~') out+="</strike>";
869 else out+="</strong>";
870 AUTO_TRACE_EXIT("result={}",i+2);
871 return static_cast<int>(i+2);
872 }
873 i++;
874 }
875 return 0;
876}

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

882{
883 AUTO_TRACE("data='{}' c={}",Trace::trunc(data),c);
884 size_t i = 0;
885 const size_t size = data.size();
886
887 while (i<size)
888 {
889 size_t len = findEmphasisChar(data.substr(i), c, 3);
890 if (len==0)
891 {
892 return 0;
893 }
894 i+=len;
895
896 /* skip whitespace preceded symbols */
897 if (data[i]!=c || data[i-1]==' ' || data[i-1]=='\n')
898 {
899 continue;
900 }
901
902 if (i+2<size && data[i+1]==c && data[i+2]==c)
903 {
904 out+="<em><strong>";
905 processInline(data.substr(0,i));
906 out+="</strong></em>";
907 AUTO_TRACE_EXIT("result={}",i+3);
908 return static_cast<int>(i+3);
909 }
910 else if (i+1<size && data[i+1]==c)
911 {
912 // double symbol found, handing over to emph1
913 len = processEmphasis1(std::string_view(data.data()-2, size+2), c);
914 if (len==0)
915 {
916 return 0;
917 }
918 else
919 {
920 AUTO_TRACE_EXIT("result={}",len-2);
921 return static_cast<int>(len - 2);
922 }
923 }
924 else
925 {
926 // single symbol found, handing over to emph2
927 len = processEmphasis2(std::string_view(data.data()-1, size+1), c);
928 if (len==0)
929 {
930 return 0;
931 }
932 else
933 {
934 AUTO_TRACE_EXIT("result={}",len-1);
935 return static_cast<int>(len - 1);
936 }
937 }
938 }
939 return 0;
940}

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

1100{
1101 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1102 return processHtmlTagWrite(data,offset,true);
1103}

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

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

1788{
1789 AUTO_TRACE("data='{}'",Trace::trunc(data));
1790 size_t i=0;
1791 size_t end=0;
1792 Action_t action;
1793 const size_t size = data.size();
1794 while (i<size)
1795 {
1796 // skip over characters that do not trigger a specific action
1797 while (end<size && ((action=actions[static_cast<uint8_t>(data[end])])==nullptr)) end++;
1798 // and add them to the output
1799 out+=data.substr(i,end-i);
1800 if (end>=size) break;
1801 i=end;
1802 // do the action matching a special character at i
1803 int iend = action(data.substr(i),i);
1804 if (iend<=0) // update end
1805 {
1806 end=i+1-iend;
1807 }
1808 else // skip until end
1809 {
1810 i+=iend;
1811 end=i;
1812 }
1813 }
1814}
std::function< int(std::string_view, size_t)> Action_t
Definition markdown.cpp:172

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

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

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(), Markdown, 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 943 of file markdown.cpp.

944{
945 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
946 const size_t size = data.size();
947 // precondition: data[0]=='-'
948 size_t i=1;
949 int count=1;
950 if (i<size && data[i]=='-') // found --
951 {
952 count++,i++;
953 }
954 if (i<size && data[i]=='-') // found ---
955 {
956 count++,i++;
957 }
958 if (i<size && data[i]=='-') // found ----
959 {
960 count++;
961 }
962 if (count>=2 && offset>=2 && literal_at(data.data()-2,"<!"))
963 { AUTO_TRACE_EXIT("result={}",1-count); return 1-count; } // start HTML comment
964 if (count==2 && size > 2 && data[2]=='>')
965 { return 0; } // end HTML comment
966 if (count==3 && size > 3 && data[3]=='>')
967 { return 0; } // end HTML comment
968 if (count==2 && (offset<8 || !literal_at(data.data()-8,"operator"))) // -- => ndash
969 {
970 out+="&ndash;";
971 AUTO_TRACE_EXIT("result=2");
972 return 2;
973 }
974 else if (count==3) // --- => ndash
975 {
976 out+="&mdash;";
977 AUTO_TRACE_EXIT("result=3");
978 return 3;
979 }
980 // not an ndash or mdash
981 return 0;
982}

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

3086{
3087 AUTO_TRACE("data='{}' refIndex='{}'",Trace::trunc(data),refIndent);
3088 out.clear();
3089 size_t i=0,end=0;
3090 size_t pi=std::string::npos;
3091 bool newBlock = false;
3092 bool insideList = false;
3093 size_t currentIndent = refIndent;
3094 size_t listIndent = refIndent;
3095 const size_t size = data.size();
3096 QCString lang;
3097 while (i<size)
3098 {
3099 end = findEndOfLine(data,i);
3100 // line is now found at [i..end)
3101
3102 size_t lineIndent=0;
3103 while (lineIndent<end && data[i+lineIndent]==' ') lineIndent++;
3104 //printf("** lineIndent=%d line=(%s)\n",lineIndent,qPrint(QCString(data+i).left(end-i)));
3105
3106 if (newBlock)
3107 {
3108 //printf("** end of block\n");
3109 if (insideList && lineIndent<currentIndent) // end of list
3110 {
3111 //printf("** end of list\n");
3112 currentIndent = refIndent;
3113 insideList = false;
3114 }
3115 newBlock = false;
3116 }
3117
3118 if ((listIndent=isListMarker(data.substr(i,end-i)))) // see if we need to increase the indent level
3119 {
3120 if (listIndent<currentIndent+4)
3121 {
3122 //printf("** start of list\n");
3123 insideList = true;
3124 currentIndent = listIndent;
3125 }
3126 }
3127 else if (isEndOfList(data.substr(i,end-i)))
3128 {
3129 //printf("** end of list\n");
3130 insideList = false;
3131 currentIndent = listIndent;
3132 }
3133 else if (isEmptyLine(data.substr(i,end-i)))
3134 {
3135 //printf("** new block\n");
3136 newBlock = true;
3137 }
3138 //printf("currentIndent=%d listIndent=%d refIndent=%d\n",currentIndent,listIndent,refIndent);
3139
3140 if (pi!=std::string::npos)
3141 {
3142 size_t blockStart=0, blockEnd=0, blockOffset=0;
3143 if (isFencedCodeBlock(data.substr(pi),currentIndent,lang,blockStart,blockEnd,blockOffset))
3144 {
3145 auto addSpecialCommand = [&](const QCString &startCmd,const QCString &endCmd)
3146 {
3147 size_t cmdPos = pi+blockStart+1;
3148 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3149 size_t ii = 0;
3150 int nl = 1;
3151 // check for absence of start command, either @start<cmd>, or \\start<cmd>
3152 while (ii<pl.length() && qisspace(pl[ii]))
3153 {
3154 if (pl[ii]=='\n') nl++;
3155 ii++; // skip leading whitespace
3156 }
3157 bool addNewLines = false;
3158 if (ii+startCmd.length()>=pl.length() || // no room for start command
3159 (pl[ii]!='\\' && pl[ii]!='@') || // no @ or \ after whitespace
3160 qstrncmp(pl.data()+ii+1,startCmd.data(),startCmd.length())!=0) // no start command
3161 {
3162 // input: output:
3163 // ----------------------------------------------------
3164 // ```{plantuml} => @startuml
3165 // A->B A->B
3166 // ``` @enduml
3167 // ----------------------------------------------------
3168 pl = "@"+startCmd+"\n" + pl + "@"+endCmd;
3169 addNewLines = false;
3170 }
3171 else // we have a @start... command inside the code block
3172 {
3173 // input: output:
3174 // ----------------------------------------------------
3175 // ```{plantuml} \n
3176 // \n
3177 // @startuml => @startuml
3178 // A->B A->B
3179 // @enduml @enduml
3180 // ``` \n
3181 // ----------------------------------------------------
3182 addNewLines = true;
3183 }
3184 if (addNewLines) for (int j=0;j<nl;j++) out+='\n';
3185 processSpecialCommand(pl.view().substr(ii),ii);
3186 if (addNewLines) out+='\n';
3187 };
3188
3189 if (!Config_getString(PLANTUML_JAR_PATH).isEmpty() && lang=="plantuml")
3190 {
3191 addSpecialCommand("startuml","enduml");
3192 }
3193 else if (Config_getBool(HAVE_DOT) && lang=="dot")
3194 {
3195 addSpecialCommand("dot","enddot");
3196 }
3197 else if (lang=="msc") // msc is built-in
3198 {
3199 addSpecialCommand("msc","endmsc");
3200 }
3201 else // normal code block
3202 {
3203 writeFencedCodeBlock(data.substr(pi),lang.view(),blockStart,blockEnd);
3204 }
3205 i=pi+blockOffset;
3206 pi=std::string::npos;
3207 end=i+1;
3208 continue;
3209 }
3210 else if (isBlockQuote(data.substr(pi,i-pi),currentIndent))
3211 {
3212 i = pi+writeBlockQuote(data.substr(pi));
3213 pi=std::string::npos;
3214 end=i+1;
3215 continue;
3216 }
3217 else
3218 {
3219 //printf("quote out={%s}\n",QCString(data+pi).left(i-pi).data());
3220 out+=data.substr(pi,i-pi);
3221 }
3222 }
3223 pi=i;
3224 i=end;
3225 }
3226 if (pi!=std::string::npos && pi<size) // deal with the last line
3227 {
3228 if (isBlockQuote(data.substr(pi),currentIndent))
3229 {
3230 writeBlockQuote(data.substr(pi));
3231 }
3232 else
3233 {
3234 out+=data.substr(pi);
3235 }
3236 }
3237
3238 //printf("Process quotations\n---- input ----\n%s\n---- output ----\n%s\n------------\n",
3239 // qPrint(s),prv->out.get());
3240
3241 return out;
3242}
#define Config_getBool(name)
Definition config.h:33
#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 985 of file markdown.cpp.

986{
987 AUTO_TRACE("data='{}'",Trace::trunc(data));
988 const size_t size = data.size();
989 size_t i=1;
990 int nl=0;
991 while (i<size && data[i]!='"' && nl<2)
992 {
993 if (data[i]=='\n') nl++;
994 i++;
995 }
996 if (i<size && data[i]=='"' && nl<2)
997 {
998 out+=data.substr(0,i+1);
999 AUTO_TRACE_EXIT("result={}",i+2);
1000 return static_cast<int>(i+1);
1001 }
1002 // not a quoted section
1003 return 0;
1004}

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

1715{
1716 AUTO_TRACE("{}",Trace::trunc(data));
1717 const size_t size = data.size();
1718 size_t i=1;
1719 QCString endBlockName = isBlockCommand(data,offset);
1720 if (!endBlockName.isEmpty())
1721 {
1722 AUTO_TRACE_ADD("endBlockName={}",endBlockName);
1723 size_t l = endBlockName.length();
1724 while (i+l<size)
1725 {
1726 if ((data[i]=='\\' || data[i]=='@') && // command
1727 data[i-1]!='\\' && data[i-1]!='@') // not escaped
1728 {
1729 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
1730 {
1731 //printf("found end at %d\n",i);
1732 addStrEscapeUtf8Nbsp(data.substr(0,i+1+l));
1733 AUTO_TRACE_EXIT("result={}",i+1+l);
1734 return static_cast<int>(i+1+l);
1735 }
1736 }
1737 i++;
1738 }
1739 }
1740 size_t endPos = isSpecialCommand(data,offset);
1741 if (endPos>0)
1742 {
1743 out+=data.substr(0,endPos);
1744 return static_cast<int>(endPos);
1745 }
1746 if (size>1 && data[0]=='\\') // escaped characters
1747 {
1748 char c=data[1];
1749 if (c=='[' || c==']' || c=='*' || c=='(' || c==')' || c=='`' || c=='_')
1750 {
1751 out+=data[1];
1752 AUTO_TRACE_EXIT("2");
1753 return 2;
1754 }
1755 else if (c=='\\' || c=='@')
1756 {
1757 out+=data.substr(0,2);
1758 AUTO_TRACE_EXIT("2");
1759 return 2;
1760 }
1761 else if (c=='-' && size>3 && data[2]=='-' && data[3]=='-') // \---
1762 {
1763 out+=data.substr(1,3);
1764 AUTO_TRACE_EXIT("2");
1765 return 4;
1766 }
1767 else if (c=='-' && size>2 && data[2]=='-') // \--
1768 {
1769 out+=data.substr(1,2);
1770 AUTO_TRACE_EXIT("3");
1771 return 3;
1772 }
1773 }
1774 else if (size>1 && data[0]=='@') // escaped characters
1775 {
1776 char c=data[1];
1777 if (c=='\\' || c=='@')
1778 {
1779 out+=data.substr(0,2);
1780 AUTO_TRACE_EXIT("2");
1781 return 2;
1782 }
1783 }
1784 return 0;
1785}
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:47
size_t isSpecialCommand(std::string_view data, size_t offset)
Definition markdown.cpp:429
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 2770 of file markdown.cpp.

2771{
2772 AUTO_TRACE("data='{}'",Trace::trunc(data));
2773 size_t i=0;
2774 int curLevel=0;
2775 size_t end=0;
2776 const size_t size = data.size();
2777 std::string startCmd;
2778 int isGitHubAlert = false;
2779 int isGitHubFirst = false;
2780 while (i<size)
2781 {
2782 // find end of this line
2783 end=i+1;
2784 while (end<=size && data[end-1]!='\n') end++;
2785 size_t j=i;
2786 int level=0;
2787 size_t indent=i;
2788 // compute the quoting level
2789 while (j<end && (data[j]==' ' || data[j]=='>'))
2790 {
2791 if (data[j]=='>') { level++; indent=j+1; }
2792 else if (j>0 && data[j-1]=='>') indent=j+1;
2793 j++;
2794 }
2795 if (indent>0 && j>0 && data[j-1]=='>' &&
2796 !(j==size || data[j]=='\n')) // disqualify last > if not followed by space
2797 {
2798 indent--;
2799 level--;
2800 j--;
2801 }
2802 AUTO_TRACE_ADD("indent={} i={} j={} end={} level={} line={}",indent,i,j,end,level,Trace::trunc(&data[i]));
2803 if (level==0 && j<end-1 && !isListMarker(data.substr(j)) && !isHRuler(data.substr(j)))
2804 {
2805 level = curLevel; // lazy
2806 }
2807 if (level==1)
2808 {
2809 QCString txt = stripWhiteSpace(data.substr(indent,end-indent));
2810 auto it = g_quotationHeaderMap.find(txt.lower().str()); // TODO: in C++20 the std::string can be dropped
2811 if (it != g_quotationHeaderMap.end())
2812 {
2813 isGitHubAlert = true;
2814 isGitHubFirst = true;
2815 startCmd = it->second;
2816 }
2817 }
2818 if (level>curLevel) // quote level increased => add start markers
2819 {
2820 if (level!=1 || !isGitHubAlert) // normal block quote
2821 {
2822 for (int l=curLevel;l<level-1;l++)
2823 {
2824 out+="<blockquote>";
2825 }
2826 out += "<blockquote>&zwj;"; // empty blockquotes are also shown
2827 }
2828 else if (!startCmd.empty()) // GitHub style alert
2829 {
2830 out += startCmd + " ";
2831 }
2832 }
2833 else if (level<curLevel) // quote level decreased => add end markers
2834 {
2835 int decrLevel = curLevel;
2836 if (level==0 && isGitHubAlert)
2837 {
2838 decrLevel--;
2839 }
2840 for (int l=level;l<decrLevel;l++)
2841 {
2842 out += "</blockquote>\\ilinebr ";
2843 }
2844 }
2845 if (level==0)
2846 {
2847 curLevel=0;
2848 break; // end of quote block
2849 }
2850 // copy line without quotation marks
2851 if (curLevel!=0 || !isGitHubAlert)
2852 {
2853 std::string_view txt = data.substr(indent,end-indent);
2854 if (stripWhiteSpace(txt).empty() && !startCmd.empty())
2855 {
2856 if (!isGitHubFirst) out += "<br>";
2857 out += "<br>\n";
2858 }
2859 else
2860 {
2861 out += txt;
2862 }
2863 isGitHubFirst = false;
2864 }
2865 else // GitHub alert section
2866 {
2867 out+= "\n";
2868 }
2869 curLevel=level;
2870 // proceed with next line
2871 i=end;
2872 }
2873 // end of comment within blockquote => add end markers
2874 if (isGitHubAlert) // GitHub alert doesn't have a blockquote
2875 {
2876 curLevel--;
2877 }
2878 for (int l=0;l<curLevel;l++)
2879 {
2880 out+="</blockquote>";
2881 }
2882 AUTO_TRACE_EXIT("i={}",i);
2883 return i;
2884}
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:2178
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 2922 of file markdown.cpp.

2923{
2924 AUTO_TRACE("data='{}' refIndent={}",Trace::trunc(data),refIndent);
2925 const size_t size = data.size();
2926 size_t i=0;
2927 // no need for \ilinebr here as the previous line was empty and was skipped
2928 out+="@iverbatim\n";
2929 int emptyLines=0;
2930 std::string location;
2931 while (i<size)
2932 {
2933 // find end of this line
2934 size_t end=i+1;
2935 while (end<=size && data[end-1]!='\n') end++;
2936 size_t j=i;
2937 size_t indent=0;
2938 while (j<end && data[j]==' ') j++,indent++;
2939 //printf("j=%d end=%d indent=%d refIndent=%d tabSize=%d data={%s}\n",
2940 // j,end,indent,refIndent,Config_getInt(TAB_SIZE),qPrint(QCString(data+i).left(end-i-1)));
2941 if (j==end-1) // empty line
2942 {
2943 emptyLines++;
2944 i=end;
2945 }
2946 else if (indent>=refIndent+codeBlockIndent) // enough indent to continue the code block
2947 {
2948 while (emptyLines>0) // write skipped empty lines
2949 {
2950 // add empty line
2951 out+="\n";
2952 emptyLines--;
2953 }
2954 // add code line minus the indent
2955 size_t offset = i+refIndent+codeBlockIndent;
2956 std::string lineLoc;
2957 if (skipOverFileAndLineCommands(data,codeBlockIndent,offset,lineLoc))
2958 {
2959 location = lineLoc;
2960 }
2961 out+=data.substr(offset,end-offset);
2962 i=end;
2963 }
2964 else // end of code block
2965 {
2966 break;
2967 }
2968 }
2969 out+="@endiverbatim";
2970 if (!location.empty())
2971 {
2972 out+=location;
2973 }
2974 else
2975 {
2976 out+="\\ilinebr ";
2977 }
2978 while (emptyLines>0) // write skipped empty lines
2979 {
2980 // add empty line
2981 out+="\n";
2982 emptyLines--;
2983 }
2984 AUTO_TRACE_EXIT("i={}",i);
2985 return i;
2986}
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 3062 of file markdown.cpp.

3064{
3065 AUTO_TRACE("data='{}' lang={} blockStart={} blockEnd={}",Trace::trunc(data),lang,blockStart,blockEnd);
3066 if (!lang.empty() && lang[0]=='.') lang=lang.substr(1);
3067 const size_t size=data.size();
3068 size_t i=0;
3069 while (i<size && (data[i]==' ' || data[i]=='\t'))
3070 {
3071 out+=data[i++];
3072 blockStart--;
3073 blockEnd--;
3074 }
3075 out+="@icode";
3076 if (!lang.empty())
3077 {
3078 out+="{"+lang+"}";
3079 }
3080 out+=" ";
3081 addStrEscapeUtf8Nbsp(data.substr(blockStart+i,blockEnd-blockStart));
3082 out+="@endicode ";
3083}

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

1159{
1160 AUTO_TRACE("fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1161 fmt,inline_img,explicitTitle,Trace::trunc(title),Trace::trunc(content),link,attrs);
1162 QCString attributes = getFilteredImageAttributes(fmt, attrs);
1163 out+="@image";
1164 if (inline_img)
1165 {
1166 out+="{inline}";
1167 }
1168 out+=" ";
1169 out+=fmt;
1170 out+=" ";
1171 out+=link.mid(fd ? 0 : 5);
1172 if (!explicitTitle && !content.isEmpty())
1173 {
1174 out+=" \"";
1175 out+=escapeDoubleQuotes(content);
1176 out+="\"";
1177 }
1178 else if ((content.isEmpty() || explicitTitle) && !title.isEmpty())
1179 {
1180 out+=" \"";
1181 out+=escapeDoubleQuotes(title);
1182 out+="\"";
1183 }
1184 else
1185 {
1186 out+=" ";// so the line break will not be part of the image name
1187 }
1188 if (!attributes.isEmpty())
1189 {
1190 out+=" ";
1191 out+=attributes;
1192 out+=" ";
1193 }
1194 out+="\\ilinebr ";
1195}
static QCString escapeDoubleQuotes(const QCString &s)
Definition markdown.cpp:220
static QCString getFilteredImageAttributes(std::string_view fmt, const QCString &attrs)
parse the image attributes and return attributes for given format
Definition markdown.cpp:313

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

2711{
2712 AUTO_TRACE("data='{}'",Trace::trunc(data));
2713 int level=0;
2714 QCString header;
2715 QCString id;
2716 if (isHRuler(data))
2717 {
2718 out+="<hr>\n";
2719 }
2720 else if ((level=isAtxHeader(data,header,id,TRUE)))
2721 {
2722 QCString hTag;
2723 if (!id.isEmpty())
2724 {
2725 switch (level)
2726 {
2727 case SectionType::Section: out+="@section "; break;
2728 case SectionType::Subsection: out+="@subsection "; break;
2729 case SectionType::Subsubsection: out+="@subsubsection "; break;
2730 case SectionType::Paragraph: out+="@paragraph "; break;
2731 case SectionType::Subparagraph: out+="@subparagraph "; break;
2732 case SectionType::Subsubparagraph: out+="@subsubparagraph "; break;
2733 }
2734 out+=id;
2735 out+=" ";
2736 out+=header;
2737 out+="\n";
2738 }
2739 else
2740 {
2741 hTag.sprintf("h%d",level);
2742 out+="<"+hTag+">";
2743 out+=header;
2744 out+="</"+hTag+">\n";
2745 }
2746 }
2747 else if (data.size()>0) // nothing interesting -> just output the line
2748 {
2749 size_t tmpSize = data.size();
2750 if (data[data.size()-1] == '\n') tmpSize--;
2751 out+=data.substr(0,tmpSize);
2752
2753 if (hasLineBreak(data))
2754 {
2755 out+="\\ilinebr<br>";
2756 }
2757 if (tmpSize != data.size()) out+='\n';
2758 }
2759}
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 2491 of file markdown.cpp.

2492{
2493 AUTO_TRACE("data='{}'",Trace::trunc(data));
2494 const size_t size = data.size();
2495
2496 size_t columns=0, start=0, end=0;
2497 size_t i = findTableColumns(data,start,end,columns);
2498 size_t headerStart = start;
2499 size_t headerEnd = end;
2500
2501 // read cell alignments
2502 size_t cc = 0;
2503 size_t ret = findTableColumns(data.substr(i),start,end,cc);
2504 size_t k=0;
2505 std::vector<int> columnAlignment(columns);
2506
2507 bool leftMarker=false, rightMarker=false, startFound=false;
2508 size_t j=start+i;
2509 while (j<=end+i)
2510 {
2511 if (!startFound)
2512 {
2513 if (data[j]==':') { leftMarker=TRUE; startFound=TRUE; }
2514 if (data[j]=='-') startFound=TRUE;
2515 //printf(" data[%d]=%c startFound=%d\n",j,data[j],startFound);
2516 }
2517 if (data[j]=='-') rightMarker=FALSE;
2518 else if (data[j]==':') rightMarker=TRUE;
2519 if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2520 {
2521 if (k<columns)
2522 {
2523 columnAlignment[k] = markersToAlignment(leftMarker,rightMarker);
2524 //printf("column[%d] alignment=%d\n",k,columnAlignment[k]);
2525 leftMarker=FALSE;
2526 rightMarker=FALSE;
2527 startFound=FALSE;
2528 }
2529 k++;
2530 }
2531 j++;
2532 }
2533 if (k<columns)
2534 {
2535 columnAlignment[k] = markersToAlignment(leftMarker,rightMarker);
2536 //printf("column[%d] alignment=%d\n",k,columnAlignment[k]);
2537 }
2538 // proceed to next line
2539 i+=ret;
2540
2541 // Store the table cell information by row then column. This
2542 // allows us to handle row spanning.
2543 std::vector<std::vector<TableCell> > tableContents;
2544
2545 size_t m = headerStart;
2546 std::vector<TableCell> headerContents(columns);
2547 for (k=0;k<columns;k++)
2548 {
2549 while (m<=headerEnd && (data[m]!='|' || (m>0 && data[m-1]=='\\')))
2550 {
2551 headerContents[k].cellText += data[m++];
2552 }
2553 m++;
2554 // do the column span test before stripping white space
2555 // || is spanning columns, | | is not
2556 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2557 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2558 }
2559 tableContents.push_back(headerContents);
2560
2561 // write table cells
2562 while (i<size)
2563 {
2564 ret = findTableColumns(data.substr(i),start,end,cc);
2565 if (cc!=columns) break; // end of table
2566
2567 j=start+i;
2568 k=0;
2569 std::vector<TableCell> rowContents(columns);
2570 while (j<=end+i)
2571 {
2572 if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2573 {
2574 // do the column span test before stripping white space
2575 // || is spanning columns, | | is not
2576 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2577 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2578 k++;
2579 } // if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\')))
2580 else
2581 {
2582 rowContents[k].cellText += data[j];
2583 } // else { if (j<=end+i && (data[j]=='|' && (j==0 || data[j-1]!='\\'))) }
2584 j++;
2585 } // while (j<=end+i)
2586 // do the column span test before stripping white space
2587 // || is spanning columns, | | is not
2588 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2589 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2590 tableContents.push_back(rowContents);
2591
2592 // proceed to next line
2593 i+=ret;
2594 }
2595
2596 out+="<table class=\"markdownTable\">";
2597 QCString cellTag("th"), cellClass("class=\"markdownTableHead");
2598 for (size_t row = 0; row < tableContents.size(); row++)
2599 {
2600 if (row)
2601 {
2602 if (row % 2)
2603 {
2604 out+="\n<tr class=\"markdownTableRowOdd\">";
2605 }
2606 else
2607 {
2608 out+="\n<tr class=\"markdownTableRowEven\">";
2609 }
2610 }
2611 else
2612 {
2613 out+="\n <tr class=\"markdownTableHead\">";
2614 }
2615 for (size_t c = 0; c < columns; c++)
2616 {
2617 // save the cell text for use after column span computation
2618 QCString cellText(tableContents[row][c].cellText);
2619
2620 // Row span handling. Spanning rows will contain a caret ('^').
2621 // If the current cell contains just a caret, this is part of an
2622 // earlier row's span and the cell should not be added to the
2623 // output.
2624 if (tableContents[row][c].cellText == "^")
2625 {
2626 continue;
2627 }
2628 if (tableContents[row][c].colSpan)
2629 {
2630 int cr = static_cast<int>(c);
2631 while ( cr >= 0 && tableContents[row][cr].colSpan)
2632 {
2633 cr--;
2634 };
2635 if (cr >= 0 && tableContents[row][cr].cellText == "^") continue;
2636 }
2637 size_t rowSpan = 1, spanRow = row+1;
2638 while ((spanRow < tableContents.size()) &&
2639 (tableContents[spanRow][c].cellText == "^"))
2640 {
2641 spanRow++;
2642 rowSpan++;
2643 }
2644
2645 out+=" <" + cellTag + " " + cellClass;
2646 // use appropriate alignment style
2647 switch (columnAlignment[c])
2648 {
2649 case AlignLeft: out+="Left\""; break;
2650 case AlignRight: out+="Right\""; break;
2651 case AlignCenter: out+="Center\""; break;
2652 case AlignNone: out+="None\""; break;
2653 }
2654
2655 if (rowSpan > 1)
2656 {
2657 QCString spanStr;
2658 spanStr.setNum(rowSpan);
2659 out+=" rowspan=\"" + spanStr + "\"";
2660 }
2661 // Column span handling, assumes that column spans will have
2662 // empty strings, which would indicate the sequence "||", used
2663 // to signify spanning columns.
2664 size_t colSpan = 1;
2665 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2666 {
2667 c++;
2668 colSpan++;
2669 }
2670 if (colSpan > 1)
2671 {
2672 QCString spanStr;
2673 spanStr.setNum(colSpan);
2674 out+=" colspan=\"" + spanStr + "\"";
2675 }
2676 // need at least one space on either side of the cell text in
2677 // order for doxygen to do other formatting
2678 out+="> " + cellText + " \\ilinebr </" + cellTag + ">";
2679 }
2680 cellTag = "td";
2681 cellClass = "class=\"markdownTableBody";
2682 out+=" </tr>";
2683 }
2684 out+="</table>\n";
2685
2686 AUTO_TRACE_EXIT("i={}",i);
2687 return i;
2688}
QCString & setNum(short n)
Definition qcstring.h:444
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:292
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:194
@ AlignNone
Definition markdown.cpp:194
@ AlignRight
Definition markdown.cpp:194
@ AlignCenter
Definition markdown.cpp:194

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

Referenced by processInline().

◆ fileName

QCString Markdown::Private::fileName

Definition at line 175 of file markdown.cpp.

Referenced by extractTitleId(), and processLink().

◆ indentLevel

int Markdown::Private::indentLevel =0

Definition at line 177 of file markdown.cpp.

Referenced by isAtxHeader(), and isHeaderline().

◆ lineNr

int Markdown::Private::lineNr = 0

Definition at line 176 of file markdown.cpp.

Referenced by extractTitleId().

◆ linkRefs

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

Definition at line 174 of file markdown.cpp.

Referenced by processBlocks(), and processLink().

◆ out


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