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

Classes

struct  LinkRef

Public Member Functions

 Private (const QCString &fn, int line, int indent)
QCString processQuotations (std::string_view data, size_t refIndent)
QCString processBlocks (std::string_view data, size_t indent)
QCString isBlockCommand (std::string_view data, size_t offset)
size_t isSpecialCommand (std::string_view data, size_t offset)
size_t findEndOfLine (std::string_view data, size_t offset)
int processHtmlTagWrite (std::string_view data, size_t offset, bool doWrite)
 Process a HTML tag.
int processHtmlTag (std::string_view data, size_t offset)
int processEmphasis (std::string_view data, size_t offset)
int processEmphasis1 (std::string_view data, char c)
 process single emphasis
int processEmphasis2 (std::string_view data, char c)
 process double emphasis
int processEmphasis3 (std::string_view data, char c)
 Parsing triple emphasis.
int processNmdash (std::string_view data, size_t offset)
 Process ndash and mdashes.
int processQuoted (std::string_view data, size_t offset)
 Process quoted section "...", can contain one embedded newline.
int processCodeSpan (std::string_view data, size_t offset)
  ` parsing a code span (assuming codespan != 0)
int processSpecialCommand (std::string_view data, size_t offset)
int processLink (std::string_view data, size_t offset)
size_t findEmphasisChar (std::string_view, char c, size_t c_size)
 looks for the next emph char, skipping other constructs, and stopping when either it is found, or we are at the end of a paragraph.
void addStrEscapeUtf8Nbsp (std::string_view data)
void processInline (std::string_view data)
void writeMarkdownImage (std::string_view fmt, bool inline_img, bool explicitTitle, const QCString &title, const QCString &content, const QCString &link, const QCString &attributes, const FileDef *fd)
int isHeaderline (std::string_view data, bool allowAdjustLevel)
 returns whether the line is a setext-style hdr underline
int isAtxHeader (std::string_view data, QCString &header, QCString &id, bool allowAdjustLevel, bool *pIsIdGenerated=nullptr)
void writeOneLineHeaderOrRuler (std::string_view data)
void writeFencedCodeBlock (std::string_view data, std::string_view lang, size_t blockStart, size_t blockEnd)
size_t writeBlockQuote (std::string_view data)
size_t writeCodeBlock (std::string_view, size_t refIndent)
size_t writeTableBlock (std::string_view data)
QCString extractTitleId (QCString &title, int level, bool *pIsIdGenerated=nullptr)

Public Attributes

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

Detailed Description

Definition at line 132 of file markdown.cpp.

Constructor & Destructor Documentation

◆ Private()

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

Definition at line 134 of file markdown.cpp.

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

References fileName, indentLevel, and lineNr.

Member Function Documentation

◆ addStrEscapeUtf8Nbsp()

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

Definition at line 1814 of file markdown.cpp.

1815{
1816 AUTO_TRACE("{}",Trace::trunc(data));
1817 if (Portable::strnstr(data.data(),g_doxy_nbsp,data.size())==nullptr) // no escape needed -> fast
1818 {
1819 out+=data;
1820 }
1821 else // escape needed -> slow
1822 {
1823 out+=substitute(QCString(data),g_doxy_nbsp,g_utf8_nbsp);
1824 }
1825}
#define AUTO_TRACE(...)
Definition docnode.cpp:48
static const char * g_doxy_nbsp
Definition markdown.cpp:220
static const char * g_utf8_nbsp
Definition markdown.cpp:219
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
Definition portable.cpp:600
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571

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

Referenced by processSpecialCommand(), and writeFencedCodeBlock().

◆ extractTitleId()

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

Definition at line 2115 of file markdown.cpp.

2116{
2117 AUTO_TRACE("title={} level={}",Trace::trunc(title),level);
2118 // match e.g. '{#id-b11} ' and capture 'id-b11'
2119 static const reg::Ex r2(R"({#(\a[\w-]*)}\s*$)");
2120 reg::Match match;
2121 std::string ti = title.str();
2122 if (reg::search(ti,match,r2))
2123 {
2124 std::string id = match[1].str();
2125 title = title.left(match.position());
2126 if (AnchorGenerator::instance().reserve(id)>0)
2127 {
2128 warn(fileName, lineNr, "An automatically generated id already has the name '{}'!", id);
2129 }
2130 //printf("found match id='%s' title=%s\n",qPrint(id),qPrint(title));
2131 AUTO_TRACE_EXIT("id={}",id);
2132 return id;
2133 }
2134 if (((level>0) && (level<=Config_getInt(TOC_INCLUDE_HEADINGS))) || (Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2135 {
2136 QCString id = AnchorGenerator::instance().generate(ti);
2137 if (pIsIdGenerated) *pIsIdGenerated=true;
2138 //printf("auto-generated id='%s' title='%s'\n",qPrint(id),qPrint(title));
2139 AUTO_TRACE_EXIT("id={}",id);
2140 return id;
2141 }
2142 //printf("no id found in title '%s'\n",qPrint(title));
2143 return "";
2144}
static AnchorGenerator & instance()
Returns the singleton instance.
Definition anchor.cpp:38
std::string generate(const std::string &title)
generates an anchor for a section with title.
Definition anchor.cpp:53
const std::string & str() const
Definition qcstring.h:552
QCString left(size_t len) const
Definition qcstring.h:229
#define Config_getInt(name)
Definition config.h:34
#define Config_getEnum(name)
Definition config.h:35
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:50
#define warn(file, line, fmt,...)
Definition message.h:97
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:844
bool match(std::string_view str, Match &match, const Ex &re)
Matches a given string str for a match against regular expression re.
Definition regex.cpp:855

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

Referenced by isAtxHeader(), and processBlocks().

◆ findEmphasisChar()

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

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

Definition at line 736 of file markdown.cpp.

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

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

3147{
3148 AUTO_TRACE("data='{}'",Trace::trunc(data));
3149 // find end of the line
3150 const size_t size = data.size();
3151 size_t nb=0, end=offset+1, j=0;
3152 while (end<=size && (j=isNewline(data.substr(end-1)))==0)
3153 {
3154 // while looking for the end of the line we might encounter a block
3155 // that needs to be passed unprocessed.
3156 if ((data[end-1]=='\\' || data[end-1]=='@') && // command
3157 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@')) // not escaped
3158 )
3159 {
3160 QCString endBlockName = isBlockCommand(data.substr(end-1),end-1);
3161 end++;
3162 if (!endBlockName.isEmpty())
3163 {
3164 size_t l = endBlockName.length();
3165 for (;end+l+1<size;end++) // search for end of block marker
3166 {
3167 if ((data[end]=='\\' || data[end]=='@') &&
3168 data[end-1]!='\\' && data[end-1]!='@'
3169 )
3170 {
3171 if (qstrncmp(&data[end+1],endBlockName.data(),l)==0)
3172 {
3173 // found end marker, skip over this block
3174 //printf("feol.block out={%s}\n",qPrint(QCString(data+i).left(end+l+1-i)));
3175 end = end + l + 2;
3176 break;
3177 }
3178 }
3179 }
3180 }
3181 }
3182 else if (nb==0 && data[end-1]=='<' && size>=6 && end+6<size &&
3183 (end<=1 || (data[end-2]!='\\' && data[end-2]!='@'))
3184 )
3185 {
3186 if (tolower(data[end])=='p' && tolower(data[end+1])=='r' &&
3187 tolower(data[end+2])=='e' && (data[end+3]=='>' || data[end+3]==' ')) // <pre> tag
3188 {
3189 // skip part until including </pre>
3190 end = end + processHtmlTagWrite(data.substr(end-1),end-1,false);
3191 break;
3192 }
3193 else
3194 {
3195 end++;
3196 }
3197 }
3198 else if (nb==0 && data[end-1]=='`')
3199 {
3200 while (end <= size && data[end - 1] == '`')
3201 {
3202 end++;
3203 nb++;
3204 }
3205 }
3206 else if (nb>0 && data[end-1]=='`')
3207 {
3208 size_t enb=0;
3209 while (end <= size && data[end - 1] == '`')
3210 {
3211 end++;
3212 enb++;
3213 }
3214 if (enb==nb) nb=0;
3215 }
3216 else
3217 {
3218 end++;
3219 }
3220 }
3221 if (j>0) end+=j-1;
3222 AUTO_TRACE_EXIT("offset={} end={}",offset,end);
3223 return end;
3224}
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
size_t isNewline(std::string_view data)
Definition markdown.cpp:227
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 2147 of file markdown.cpp.

2149{
2150 AUTO_TRACE("data='{}' header={} id={} allowAdjustLevel={}",Trace::trunc(data),Trace::trunc(header),id,allowAdjustLevel);
2151 size_t i = 0;
2152 int level = 0, blanks=0;
2153 const size_t size = data.size();
2154
2155 // find start of header text and determine heading level
2156 while (i<size && data[i]==' ') i++;
2157 if (i>=size || data[i]!='#')
2158 {
2159 return 0;
2160 }
2161 while (i < size && data[i] == '#')
2162 {
2163 i++;
2164 level++;
2165 }
2166 if (level>SectionType::MaxLevel) // too many #'s -> no section
2167 {
2168 return 0;
2169 }
2170 while (i < size && data[i] == ' ')
2171 {
2172 i++;
2173 blanks++;
2174 }
2175 if (level==1 && blanks==0)
2176 {
2177 return 0; // special case to prevent #someid seen as a header (see bug 671395)
2178 }
2179
2180 // find end of header text
2181 size_t end=i;
2182 while (end<size && data[end]!='\n') end++;
2183 while (end>i && (data[end-1]=='#' || data[end-1]==' ')) end--;
2184
2185 // store result
2186 header = data.substr(i,end-i);
2187 id = extractTitleId(header, level, pIsIdGenerated);
2188 if (!id.isEmpty()) // strip #'s between title and id
2189 {
2190 int idx=static_cast<int>(header.length())-1;
2191 while (idx>=0 && (header.at(idx)=='#' || header.at(idx)==' ')) idx--;
2192 header=header.left(idx+1);
2193 }
2194
2195 if (allowAdjustLevel && level==1 && indentLevel==-1)
2196 {
2197 // in case we find a `# Section` on a markdown page that started with the same level
2198 // header, we no longer need to artificially decrease the paragraph level.
2199 // So both
2200 // -------------------
2201 // # heading 1 <-- here we set g_indentLevel to -1
2202 // # heading 2 <-- here we set g_indentLevel back to 0 such that this will be a @section
2203 // -------------------
2204 // and
2205 // -------------------
2206 // # heading 1 <-- here we set g_indentLevel to -1
2207 // ## heading 2 <-- here we keep g_indentLevel at -1 such that @subsection will be @section
2208 // -------------------
2209 // will convert to
2210 // -------------------
2211 // @page md_page Heading 1
2212 // @section autotoc_md1 Heading 2
2213 // -------------------
2214
2215 indentLevel=0;
2216 }
2217 int res = level+indentLevel;
2218 AUTO_TRACE_EXIT("result={}",res);
2219 return res;
2220}
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 390 of file markdown.cpp.

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

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

1921{
1922 AUTO_TRACE("data='{}' allowAdjustLevel",Trace::trunc(data),allowAdjustLevel);
1923 size_t i=0, c=0;
1924 const size_t size = data.size();
1925 while (i<size && data[i]==' ') i++;
1926 if (i==size) return 0;
1927
1928 // test of level 1 header
1929 if (data[i]=='=')
1930 {
1931 while (i < size && data[i] == '=')
1932 {
1933 i++;
1934 c++;
1935 }
1936 while (i<size && data[i]==' ') i++;
1937 int level = (c>1 && (i>=size || data[i]=='\n')) ? 1 : 0;
1938 if (allowAdjustLevel && level==1 && indentLevel==-1)
1939 {
1940 // In case a page starts with a header line we use it as title, promoting it to @page.
1941 // We set g_indentLevel to -1 to promoting the other sections if they have a deeper
1942 // nesting level than the page header, i.e. @section..@subsection becomes @page..@section.
1943 // In case a section at the same level is found (@section..@section) however we need
1944 // to undo this (and the result will be @page..@section).
1945 indentLevel=0;
1946 }
1947 AUTO_TRACE_EXIT("result={}",indentLevel+level);
1948 return indentLevel+level;
1949 }
1950 // test of level 2 header
1951 if (data[i]=='-')
1952 {
1953 while (i < size && data[i] == '-')
1954 {
1955 i++;
1956 c++;
1957 }
1958 while (i<size && data[i]==' ') i++;
1959 return (c>1 && (i>=size || data[i]=='\n')) ? indentLevel+2 : 0;
1960 }
1961 return 0;
1962}

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

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

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

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

◆ processCodeSpan()

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

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

Definition at line 1700 of file markdown.cpp.

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

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

Referenced by Markdown::fill_table().

◆ processEmphasis()

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

Definition at line 1149 of file markdown.cpp.

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

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

Referenced by Markdown::fill_table().

◆ processEmphasis1()

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

process single emphasis

Definition at line 853 of file markdown.cpp.

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

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

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

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

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

1144{
1145 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
1146 return processHtmlTagWrite(data,offset,true);
1147}

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

Referenced by Markdown::fill_table().

◆ processHtmlTagWrite()

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

Process a HTML tag.

Note that

..

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

Definition at line 1049 of file markdown.cpp.

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

1891{
1892 AUTO_TRACE("data='{}'",Trace::trunc(data));
1893 size_t i=0;
1894 size_t end=0;
1895 Action_t action;
1896 const size_t size = data.size();
1897 while (i<size)
1898 {
1899 // skip over characters that do not trigger a specific action
1900 while (end<size && ((action=Markdown::actions[static_cast<uint8_t>(data[end])])==nullptr)) end++;
1901 // and add them to the output
1902 out+=data.substr(i,end-i);
1903 if (end>=size) break;
1904 i=end;
1905 // do the action matching a special character at i
1906 int iend = action(*this,data.substr(i),i);
1907 if (iend<=0) // update end
1908 {
1909 end=i+1-iend;
1910 }
1911 else // skip until end
1912 {
1913 i+=iend;
1914 end=i;
1915 }
1916 }
1917}
static ActionTable_t actions
Definition markdown.h:47
std::function< int(Private &, std::string_view, size_t)> Action_t
Definition markdown.h:44

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

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

◆ processLink()

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

Definition at line 1241 of file markdown.cpp.

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

References FileInfo::absFilePath(), AnchorGenerator::addPrefixIfNeeded(), QCString::at(), AUTO_TRACE, AUTO_TRACE_EXIT, Mappers::cmdMapper, Config_getEnum, Config_getInt, FileInfo::exists(), externalLinkTarget(), FALSE, FileInfo::fileName(), fileName, QCString::find(), findFileDef(), getLanguageFromFileName(), Doxygen::imageNameLinkedMap, Portable::isAbsolutePath(), CommentScanner::isCommand(), QCString::isEmpty(), isId(), 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(), UNKNOWN, and writeMarkdownImage().

Referenced by Markdown::fill_table().

◆ processNmdash()

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

Process ndash and mdashes.

Definition at line 981 of file markdown.cpp.

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

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

Referenced by Markdown::fill_table().

◆ processQuotations()

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

Definition at line 3249 of file markdown.cpp.

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

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

◆ processQuoted()

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

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

Definition at line 1025 of file markdown.cpp.

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

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

Referenced by Markdown::fill_table().

◆ processSpecialCommand()

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

Definition at line 1827 of file markdown.cpp.

1828{
1829 AUTO_TRACE("{}",Trace::trunc(data));
1830 const size_t size = data.size();
1831 size_t i=1;
1832 QCString endBlockName = isBlockCommand(data,offset);
1833 if (!endBlockName.isEmpty())
1834 {
1835 AUTO_TRACE_ADD("endBlockName={}",endBlockName);
1836 size_t l = endBlockName.length();
1837 while (i+l<size)
1838 {
1839 if ((data[i]=='\\' || data[i]=='@') && // command
1840 data[i-1]!='\\' && data[i-1]!='@') // not escaped
1841 {
1842 if (qstrncmp(&data[i+1],endBlockName.data(),l)==0)
1843 {
1844 //printf("found end at %d\n",i);
1845 addStrEscapeUtf8Nbsp(data.substr(0,i+1+l));
1846 AUTO_TRACE_EXIT("result={}",i+1+l);
1847 return static_cast<int>(i+1+l);
1848 }
1849 }
1850 i++;
1851 }
1852 }
1853 size_t endPos = isSpecialCommand(data,offset);
1854 if (endPos>0)
1855 {
1856 out+=data.substr(0,endPos);
1857 return static_cast<int>(endPos);
1858 }
1859 if (size>1 && (data[0]=='\\' || data[0]=='@')) // escaped characters
1860 {
1861 char c=data[1];
1862 if (c=='[' || c==']' || c=='*' || c=='(' || c==')' || c=='`' || c=='_')
1863 {
1864 out+=data[1];
1865 AUTO_TRACE_EXIT("2");
1866 return 2;
1867 }
1868 else if (c=='\\' || c=='@')
1869 {
1870 out+=data.substr(0,2);
1871 AUTO_TRACE_EXIT("2");
1872 return 2;
1873 }
1874 else if (c=='-' && size>3 && data[2]=='-' && data[3]=='-') // \---
1875 {
1876 out+=data.substr(1,3);
1877 AUTO_TRACE_EXIT("2");
1878 return 4;
1879 }
1880 else if (c=='-' && size>2 && data[2]=='-') // \--
1881 {
1882 out+=data.substr(1,2);
1883 AUTO_TRACE_EXIT("3");
1884 return 3;
1885 }
1886 }
1887 return 0;
1888}
size_t isSpecialCommand(std::string_view data, size_t offset)
Definition markdown.cpp:460
void addStrEscapeUtf8Nbsp(std::string_view data)

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

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

◆ writeBlockQuote()

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

Definition at line 2918 of file markdown.cpp.

2919{
2920 AUTO_TRACE("data='{}'",Trace::trunc(data));
2921 size_t i=0;
2922 int curLevel=0;
2923 size_t end=0;
2924 const size_t size = data.size();
2925 std::string startCmd;
2926 int isGitHubAlert = false;
2927 int isGitHubFirst = false;
2928 while (i<size)
2929 {
2930 // find end of this line
2931 end=i+1;
2932 while (end<=size && data[end-1]!='\n') end++;
2933 size_t j=i;
2934 int level=0;
2935 size_t indent=i;
2936 // compute the quoting level
2937 while (j<end && (data[j]==' ' || data[j]=='>'))
2938 {
2939 if (data[j]=='>') { level++; indent=j+1; }
2940 else if (j>0 && data[j-1]=='>') indent=j+1;
2941 j++;
2942 }
2943 if (indent>0 && j>0 && data[j-1]=='>' &&
2944 !(j==size || data[j]=='\n')) // disqualify last > if not followed by space
2945 {
2946 indent--;
2947 level--;
2948 j--;
2949 }
2950 AUTO_TRACE_ADD("indent={} i={} j={} end={} level={} line={}",indent,i,j,end,level,Trace::trunc(&data[i]));
2951 if (level==0 && j<end-1 && !isListMarker(data.substr(j)) && !isHRuler(data.substr(j)))
2952 {
2953 level = curLevel; // lazy
2954 }
2955 if (level==1)
2956 {
2957 QCString txt = stripWhiteSpace(data.substr(indent,end-indent));
2958 auto it = g_quotationHeaderMap.find(txt.lower().str()); // TODO: in C++20 the std::string can be dropped
2959 if (it != g_quotationHeaderMap.end())
2960 {
2961 isGitHubAlert = true;
2962 isGitHubFirst = true;
2963 startCmd = it->second;
2964 }
2965 }
2966 if (level>curLevel) // quote level increased => add start markers
2967 {
2968 if (level!=1 || !isGitHubAlert) // normal block quote
2969 {
2970 for (int l=curLevel;l<level-1;l++)
2971 {
2972 out+="<blockquote>";
2973 }
2974 out += "<blockquote>&zwj;"; // empty blockquotes are also shown
2975 }
2976 else if (!startCmd.empty()) // GitHub style alert
2977 {
2978 out += startCmd + " ";
2979 }
2980 }
2981 else if (level<curLevel) // quote level decreased => add end markers
2982 {
2983 int decrLevel = curLevel;
2984 if (level==0 && isGitHubAlert)
2985 {
2986 decrLevel--;
2987 }
2988 for (int l=level;l<decrLevel;l++)
2989 {
2990 out += "</blockquote>\\ilinebr ";
2991 }
2992 }
2993 if (level==0)
2994 {
2995 curLevel=0;
2996 break; // end of quote block
2997 }
2998 // copy line without quotation marks
2999 if (curLevel!=0 || !isGitHubAlert)
3000 {
3001 std::string_view txt = data.substr(indent,end-indent);
3002 if (stripWhiteSpace(txt).empty() && !startCmd.empty())
3003 {
3004 if (!isGitHubFirst) out += "<br>";
3005 out += "<br>\n";
3006 }
3007 else
3008 {
3009 out += txt;
3010 }
3011 isGitHubFirst = false;
3012 }
3013 else // GitHub alert section
3014 {
3015 out+= "\n";
3016 }
3017 curLevel=level;
3018 // proceed with next line
3019 i=end;
3020 }
3021 // end of comment within blockquote => add end markers
3022 if (isGitHubAlert) // GitHub alert doesn't have a blockquote
3023 {
3024 curLevel--;
3025 }
3026 for (int l=0;l<curLevel;l++)
3027 {
3028 out+="</blockquote>";
3029 }
3030 AUTO_TRACE_EXIT("i={}",i);
3031 return i;
3032}
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:2279
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 3074 of file markdown.cpp.

3075{
3076 AUTO_TRACE("data='{}' refIndent={}",Trace::trunc(data),refIndent);
3077 const size_t size = data.size();
3078 size_t i=0;
3079 // no need for \ilinebr here as the previous line was empty and was skipped
3080 out+="@iverbatim\n";
3081 int emptyLines=0;
3082 std::string location;
3083 while (i<size)
3084 {
3085 // find end of this line
3086 size_t end=i+1;
3087 while (end<=size && data[end-1]!='\n') end++;
3088 size_t j=i;
3089 size_t indent=0;
3090 while (j < end && data[j] == ' ')
3091 {
3092 j++;
3093 indent++;
3094 }
3095 //printf("j=%d end=%d indent=%d refIndent=%d tabSize=%d data={%s}\n",
3096 // j,end,indent,refIndent,Config_getInt(TAB_SIZE),qPrint(QCString(data+i).left(end-i-1)));
3097 if (j==end-1) // empty line
3098 {
3099 emptyLines++;
3100 i=end;
3101 }
3102 else if (indent>=refIndent+codeBlockIndent) // enough indent to continue the code block
3103 {
3104 while (emptyLines>0) // write skipped empty lines
3105 {
3106 // add empty line
3107 out+="\n";
3108 emptyLines--;
3109 }
3110 // add code line minus the indent
3111 size_t offset = i+refIndent+codeBlockIndent;
3112 std::string lineLoc;
3113 if (skipOverFileAndLineCommands(data,codeBlockIndent,offset,lineLoc))
3114 {
3115 location = lineLoc;
3116 }
3117 out+=data.substr(offset,end-offset);
3118 i=end;
3119 }
3120 else // end of code block
3121 {
3122 break;
3123 }
3124 }
3125 out+="@endiverbatim";
3126 if (!location.empty())
3127 {
3128 out+=location;
3129 }
3130 else
3131 {
3132 out+="\\ilinebr ";
3133 }
3134 while (emptyLines>0) // write skipped empty lines
3135 {
3136 // add empty line
3137 out+="\n";
3138 emptyLines--;
3139 }
3140 AUTO_TRACE_EXIT("i={}",i);
3141 return i;
3142}
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 3226 of file markdown.cpp.

3228{
3229 AUTO_TRACE("data='{}' lang={} blockStart={} blockEnd={}",Trace::trunc(data),lang,blockStart,blockEnd);
3230 if (!lang.empty() && lang[0]=='.') lang=lang.substr(1);
3231 const size_t size=data.size();
3232 size_t i=0;
3233 while (i<size && (data[i]==' ' || data[i]=='\t'))
3234 {
3235 out+=data[i++];
3236 blockStart--;
3237 blockEnd--;
3238 }
3239 out+="@icode";
3240 if (!lang.empty())
3241 {
3242 out+="{"+lang+"}";
3243 }
3244 out+=" ";
3245 addStrEscapeUtf8Nbsp(data.substr(blockStart+i,blockEnd-blockStart));
3246 out+="@endicode ";
3247}

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

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

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

2859{
2860 AUTO_TRACE("data='{}'",Trace::trunc(data));
2861 int level=0;
2862 QCString header;
2863 QCString id;
2864 if (isHRuler(data))
2865 {
2866 out+="<hr>\n";
2867 }
2868 else if ((level=isAtxHeader(data,header,id,TRUE)))
2869 {
2870 QCString hTag;
2871 if (!id.isEmpty())
2872 {
2873 switch (level)
2874 {
2875 case SectionType::Section: out+="@section "; break;
2876 case SectionType::Subsection: out+="@subsection "; break;
2877 case SectionType::Subsubsection: out+="@subsubsection "; break;
2878 case SectionType::Paragraph: out+="@paragraph "; break;
2879 case SectionType::Subparagraph: out+="@subparagraph "; break;
2880 case SectionType::Subsubparagraph: out+="@subsubparagraph "; break;
2881 }
2882 out+=id;
2883 out+=" ";
2884 out+=header;
2885 out+="\n";
2886 }
2887 else
2888 {
2889 hTag.sprintf("h%d",level);
2890 out+="<"+hTag+">";
2891 out+=header;
2892 out+="</"+hTag+">\n";
2893 }
2894 }
2895 else if (data.size()>0) // nothing interesting -> just output the line
2896 {
2897 size_t tmpSize = data.size();
2898 if (data[data.size()-1] == '\n') tmpSize--;
2899 out+=data.substr(0,tmpSize);
2900
2901 if (hasLineBreak(data))
2902 {
2903 out+="\\ilinebr<br>";
2904 }
2905 if (tmpSize != data.size()) out+='\n';
2906 }
2907}
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 2639 of file markdown.cpp.

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

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

Referenced by processBlocks().

Member Data Documentation

◆ fileName

QCString Markdown::Private::fileName

Definition at line 178 of file markdown.cpp.

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

◆ indentLevel

int Markdown::Private::indentLevel =0

Definition at line 180 of file markdown.cpp.

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

◆ lineNr

int Markdown::Private::lineNr = 0

Definition at line 179 of file markdown.cpp.

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

◆ linkRefs

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

Definition at line 177 of file markdown.cpp.

Referenced by processBlocks(), and processLink().

◆ out


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