Doxygen
Loading...
Searching...
No Matches
markdown.cpp File Reference
#include <stdio.h>
#include <unordered_map>
#include <functional>
#include <atomic>
#include <array>
#include <string_view>
#include "markdown.h"
#include "debug.h"
#include "util.h"
#include "doxygen.h"
#include "commentscan.h"
#include "entry.h"
#include "config.h"
#include "message.h"
#include "portable.h"
#include "regex.h"
#include "fileinfo.h"
#include "trace.h"
#include "anchor.h"
#include "stringutil.h"
Include dependency graph for markdown.cpp:

Go to the source code of this file.

Classes

struct  TableCell
struct  Markdown::Private
struct  Markdown::Private::LinkRef
struct  MarkdownOutlineParser::Private

Macros

#define AUTO_TRACE(...)
#define AUTO_TRACE_ADD(...)
#define AUTO_TRACE_EXIT(...)
#define isIdChar(c)
#define extraChar(c)
#define isOpenEmphChar(c)
#define isUtf8Nbsp(c1, c2)
#define isAllowedEmphStr(data, offset)
#define ignoreCloseEmphChar(c, cn)
#define isLiTag(i)
#define OPC(x)

Enumerations

enum class  ExplicitPageResult { explicitPage , explicitMainPage , explicitOtherPage , notExplicit }
enum  Alignment { AlignNone , AlignLeft , AlignCenter , AlignRight }

Functions

size_t isNewline (std::string_view data)
static QCString escapeDoubleQuotes (const QCString &s)
static QCString escapeSpecialChars (const QCString &s)
static Alignment markersToAlignment (bool leftMarker, bool rightMarker)
 helper function to convert presence of left and/or right alignment markers to an alignment value
static QCString getFilteredImageAttributes (std::string_view fmt, const QCString &attrs)
 parse the image attributes and return attributes for given format
static bool isBlockQuote (std::string_view data, size_t indent)
 returns true if this line starts a block quote
static size_t isLinkRef (std::string_view data, QCString &refid, QCString &link, QCString &title)
 returns end of the link ref if this is indeed a link reference.
static bool isHRuler (std::string_view data)
static bool isEmptyLine (std::string_view data)
static size_t computeIndentExcludingListMarkers (std::string_view data)
static size_t isListMarker (std::string_view data)
static bool isEndOfList (std::string_view data)
static bool isFencedCodeBlock (std::string_view data, size_t refIndent, QCString &lang, size_t &start, size_t &end, size_t &offset)
static bool isCodeBlock (std::string_view data, size_t offset, size_t &indent)
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.
static bool isTableBlock (std::string_view data)
 Returns TRUE iff data points to the start of a table block.
static bool hasLineBreak (std::string_view data)
bool skipOverFileAndLineCommands (std::string_view data, size_t indent, size_t &offset, std::string &location)
static bool isOtherPage (std::string_view data)
static ExplicitPageResult isExplicitPage (const QCString &docs)
QCString markdownFileNameToId (const QCString &fileName)
 processes string s and converts markdown into doxygen/html commands.

Variables

const char * g_utf8_nbsp = "\xc2\xa0"
const char * g_doxy_nbsp = "&_doxy_nbsp;"
const size_t codeBlockIndent = 4
static const std::unordered_map< std::string, std::string > g_quotationHeaderMap

Macro Definition Documentation

◆ AUTO_TRACE

#define AUTO_TRACE ( ...)
Value:
(void)0

Definition at line 61 of file markdown.cpp.

Referenced by escapeDoubleQuotes(), escapeSpecialChars(), and getFilteredImageAttributes().

◆ AUTO_TRACE_ADD

#define AUTO_TRACE_ADD ( ...)
Value:
(void)0

Definition at line 62 of file markdown.cpp.

◆ AUTO_TRACE_EXIT

#define AUTO_TRACE_EXIT ( ...)
Value:
(void)0

Definition at line 63 of file markdown.cpp.

Referenced by escapeDoubleQuotes(), escapeSpecialChars(), and getFilteredImageAttributes().

◆ extraChar

#define extraChar ( c)
Value:
(c=='-' || c=='+' || c=='!' || \
c=='?' || c=='$' || c=='@' || \
c=='&' || c=='*' || c=='_' || c=='%' || \
c=='[' || c=='(' || c=='.' || \
c=='>' || c==':' || c==',' || \
c==';' || c=='\'' || c=='"' || c=='`')

Definition at line 84 of file markdown.cpp.

84#define extraChar(c) \
85 (c=='-' || c=='+' || c=='!' || \
86 c=='?' || c=='$' || c=='@' || \
87 c=='&' || c=='*' || c=='_' || c=='%' || \
88 c=='[' || c=='(' || c=='.' || \
89 c=='>' || c==':' || c==',' || \
90 c==';' || c=='\'' || c=='"' || c=='`')

Referenced by Markdown::Private::processEmphasis().

◆ ignoreCloseEmphChar

#define ignoreCloseEmphChar ( c,
cn )
Value:
(c=='(' || c=='{' || c=='[' || (c=='<' && cn!='/') || \
c=='\\' || \
c=='@')

Definition at line 108 of file markdown.cpp.

108#define ignoreCloseEmphChar(c,cn) \
109 (c=='(' || c=='{' || c=='[' || (c=='<' && cn!='/') || \
110 c=='\\' || \
111 c=='@')

Referenced by Markdown::Private::findEmphasisChar().

◆ isAllowedEmphStr

#define isAllowedEmphStr ( data,
offset )
Value:
(!(offset>0 && isOpenEmphChar(data.data()[-1])) && \
!(offset>1 && isUtf8Nbsp(data.data()[-2],data.data()[-1])))
#define isUtf8Nbsp(c1, c2)
Definition markdown.cpp:99
#define isOpenEmphChar(c)
Definition markdown.cpp:93

Definition at line 102 of file markdown.cpp.

102#define isAllowedEmphStr(data,offset) \
103 (!(offset>0 && isOpenEmphChar(data.data()[-1])) && \
104 !(offset>1 && isUtf8Nbsp(data.data()[-2],data.data()[-1])))

Referenced by Markdown::Private::processEmphasis().

◆ isIdChar

#define isIdChar ( c)
Value:
((c>='a' && c<='z') || \
(c>='A' && c<='Z') || \
(c>='0' && c<='9') || \
(static_cast<unsigned char>(c)>=0x80))

Definition at line 77 of file markdown.cpp.

77#define isIdChar(c) \
78 ((c>='a' && c<='z') || \
79 (c>='A' && c<='Z') || \
80 (c>='0' && c<='9') || \
81 (static_cast<unsigned char>(c)>=0x80)) // unicode characters

Referenced by Markdown::Private::findEmphasisChar(), reg::Ex::Private::matchAt(), Markdown::Private::processCodeSpan(), Markdown::Private::processEmphasis(), and Markdown::Private::processHtmlTagWrite().

◆ isLiTag

#define isLiTag ( i)
Value:
(data[(i)]=='<' && \
(data[(i)+1]=='l' || data[(i)+1]=='L') && \
(data[(i)+2]=='i' || data[(i)+2]=='I') && \
(data[(i)+3]=='>'))

Definition at line 2177 of file markdown.cpp.

2177#define isLiTag(i) \
2178 (data[(i)]=='<' && \
2179 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2180 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2181 (data[(i)+3]=='>'))

Referenced by computeIndentExcludingListMarkers().

◆ isOpenEmphChar

#define isOpenEmphChar ( c)
Value:
(c=='\n' || c==' ' || c=='\'' || c=='<' || \
c=='>' || c=='{' || c=='(' || c=='[' || \
c==',' || c==':' || c==';')

Definition at line 93 of file markdown.cpp.

93#define isOpenEmphChar(c) \
94 (c=='\n' || c==' ' || c=='\'' || c=='<' || \
95 c=='>' || c=='{' || c=='(' || c=='[' || \
96 c==',' || c==':' || c==';')

◆ isUtf8Nbsp

#define isUtf8Nbsp ( c1,
c2 )
Value:
(c1==static_cast<char>(0xc2) && c2==static_cast<char>(0xa0))

Definition at line 99 of file markdown.cpp.

99#define isUtf8Nbsp(c1,c2) \
100 (c1==static_cast<char>(0xc2) && c2==static_cast<char>(0xa0))

◆ OPC

#define OPC ( x)
Value:
if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
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

Referenced by isOtherPage().

Enumeration Type Documentation

◆ Alignment

enum Alignment
Enumerator
AlignNone 
AlignLeft 
AlignCenter 
AlignRight 

Definition at line 202 of file markdown.cpp.

@ AlignLeft
Definition markdown.cpp:202
@ AlignNone
Definition markdown.cpp:202
@ AlignRight
Definition markdown.cpp:202
@ AlignCenter
Definition markdown.cpp:202

◆ ExplicitPageResult

enum class ExplicitPageResult
strong
Enumerator
explicitPage 

docs start with a page command

explicitMainPage 

docs start with a mainpage command

explicitOtherPage 

docs start with a dir / defgroup / addtogroup command

notExplicit 

docs doesn't start with either page or mainpage

Definition at line 66 of file markdown.cpp.

67{
68 explicitPage, /**< docs start with a page command */
69 explicitMainPage, /**< docs start with a mainpage command */
70 explicitOtherPage, /**< docs start with a dir / defgroup / addtogroup command */
71 notExplicit /**< docs doesn't start with either page or mainpage */
72};
@ explicitMainPage
docs start with a mainpage command
Definition markdown.cpp:69
@ explicitPage
docs start with a page command
Definition markdown.cpp:68
@ notExplicit
docs doesn't start with either page or mainpage
Definition markdown.cpp:71
@ explicitOtherPage
docs start with a dir / defgroup / addtogroup command
Definition markdown.cpp:70

Function Documentation

◆ computeIndentExcludingListMarkers()

size_t computeIndentExcludingListMarkers ( std::string_view data)
static

Definition at line 2185 of file markdown.cpp.

2186{
2187 AUTO_TRACE("data='{}'",Trace::trunc(data));
2188 size_t i=0;
2189 const size_t size=data.size();
2190 size_t indent=0;
2191 bool isDigit=FALSE;
2192 bool isLi=FALSE;
2193 bool listMarkerSkipped=FALSE;
2194 while (i<size &&
2195 (data[i]==' ' || // space
2196 (!listMarkerSkipped && // first list marker
2197 (data[i]=='+' || data[i]=='-' || data[i]=='*' || // unordered list char
2198 (data[i]=='#' && i>0 && data[i-1]=='-') || // -# item
2199 (isDigit=(data[i]>='1' && data[i]<='9')) || // ordered list marker?
2200 (isLi=(size>=3 && i+3<size && isLiTag(i))) // <li> tag
2201 )
2202 )
2203 )
2204 )
2205 {
2206 if (isDigit) // skip over ordered list marker '10. '
2207 {
2208 size_t j=i+1;
2209 while (j<size && ((data[j]>='0' && data[j]<='9') || data[j]=='.'))
2210 {
2211 if (data[j]=='.') // should be end of the list marker
2212 {
2213 if (j+1<size && data[j+1]==' ') // valid list marker
2214 {
2215 listMarkerSkipped=TRUE;
2216 indent+=j+1-i;
2217 i=j+1;
2218 break;
2219 }
2220 else // not a list marker
2221 {
2222 break;
2223 }
2224 }
2225 j++;
2226 }
2227 }
2228 else if (isLi)
2229 {
2230 i+=3; // skip over <li>
2231 indent+=3;
2232 listMarkerSkipped=TRUE;
2233 }
2234 else if (data[i]=='-' && size>=2 && i+2<size && data[i+1]=='#' && data[i+2]==' ')
2235 { // case "-# "
2236 listMarkerSkipped=TRUE; // only a single list marker is accepted
2237 i++; // skip over #
2238 indent++;
2239 }
2240 else if (data[i]!=' ' && i+1<size && data[i+1]==' ')
2241 { // case "- " or "+ " or "* "
2242 listMarkerSkipped=TRUE; // only a single list marker is accepted
2243 }
2244 if (data[i]!=' ' && !listMarkerSkipped)
2245 { // end of indent
2246 break;
2247 }
2248 indent++,i++;
2249 }
2250 AUTO_TRACE_EXIT("result={}",indent);
2251 return indent;
2252}
#define AUTO_TRACE(...)
Definition markdown.cpp:61
#define AUTO_TRACE_EXIT(...)
Definition markdown.cpp:63
#define isLiTag(i)
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34

References AUTO_TRACE, AUTO_TRACE_EXIT, FALSE, isLiTag, TRUE, and Trace::trunc().

Referenced by isCodeBlock(), and isListMarker().

◆ escapeDoubleQuotes()

QCString escapeDoubleQuotes ( const QCString & s)
static

Definition at line 228 of file markdown.cpp.

229{
230 AUTO_TRACE("s={}",Trace::trunc(s));
231 if (s.isEmpty()) return s;
232 QCString result;
233 const char *p=s.data();
234 char c=0, pc='\0';
235 while ((c=*p++))
236 {
237 if (c=='"' && pc!='\\') result+='\\';
238 result+=c;
239 pc=c;
240 }
241 AUTO_TRACE_EXIT("result={}",result);
242 return result;
243}
This is an alternative implementation of QCString.
Definition qcstring.h:101
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

References AUTO_TRACE, AUTO_TRACE_EXIT, QCString::data(), QCString::isEmpty(), and Trace::trunc().

Referenced by Markdown::Private::writeMarkdownImage().

◆ escapeSpecialChars()

QCString escapeSpecialChars ( const QCString & s)
static

Definition at line 246 of file markdown.cpp.

247{
248 AUTO_TRACE("s={}",Trace::trunc(s));
249 if (s.isEmpty()) return s;
250 bool insideQuote=FALSE;
251 QCString result;
252 const char *p=s.data();
253 char c=0, pc='\0';
254 while ((c=*p++))
255 {
256 switch (c)
257 {
258 case '"':
259 if (pc!='\\')
260 {
261 if (Config_getBool(MARKDOWN_STRICT))
262 {
263 result+='\\';
264 }
265 else // For Doxygen's markup style a quoted text is left untouched
266 {
267 insideQuote=!insideQuote;
268 }
269 }
270 result+=c;
271 break;
272 case '<':
273 // fall through
274 case '>':
275 if (!insideQuote)
276 {
277 result+='\\';
278 result+=c;
279 if ((p[0]==':') && (p[1]==':'))
280 {
281 result+='\\';
282 result+=':';
283 p++;
284 }
285 }
286 else
287 {
288 result+=c;
289 }
290 break;
291 case '\\': if (!insideQuote) { result+='\\'; } result+='\\'; break;
292 case '@': if (!insideQuote) { result+='\\'; } result+='@'; break;
293 // commented out next line due to regression when using % to suppress a link
294 //case '%': if (!insideQuote) { result+='\\'; } result+='%'; break;
295 case '#': if (!insideQuote) { result+='\\'; } result+='#'; break;
296 case '$': if (!insideQuote) { result+='\\'; } result+='$'; break;
297 case '&': if (!insideQuote) { result+='\\'; } result+='&'; break;
298 default:
299 result+=c; break;
300 }
301 pc=c;
302 }
303 AUTO_TRACE_EXIT("result={}",result);
304 return result;
305}
#define Config_getBool(name)
Definition config.h:33

References AUTO_TRACE, AUTO_TRACE_EXIT, Config_getBool, QCString::data(), FALSE, QCString::isEmpty(), and Trace::trunc().

Referenced by Markdown::Private::processCodeSpan().

◆ findTableColumns()

size_t findTableColumns ( std::string_view data,
size_t & start,
size_t & end,
size_t & columns )
static

Finds the location of the table's contains in the string data.

Only one line will be inspected.

Parameters
[in]datapointer to the string buffer.
[out]startoffset of the first character of the table content
[out]endoffset of the last character of the table content
[out]columnsnumber of table columns found
Returns
The offset until the next line in the buffer.

Definition at line 2470 of file markdown.cpp.

2471{
2472 AUTO_TRACE("data='{}'",Trace::trunc(data));
2473 const size_t size = data.size();
2474 size_t i=0,n=0;
2475 // find start character of the table line
2476 while (i<size && data[i]==' ') i++;
2477 if (i<size && data[i]=='|' && data[i]!='\n') i++,n++; // leading | does not count
2478 start = i;
2479
2480 // find end character of the table line
2481 size_t j = 0;
2482 while (i<size && (j = isNewline(data.substr(i)))==0) i++;
2483 size_t eol=i+j;
2484
2485 if (j>0 && i>0) i--; // move i to point before newline
2486 while (i>0 && data[i]==' ') i--;
2487 if (i>0 && data[i-1]!='\\' && data[i]=='|') i--,n++; // trailing or escaped | does not count
2488 end = i;
2489
2490 // count columns between start and end
2491 columns=0;
2492 if (end>start)
2493 {
2494 i=start;
2495 while (i<=end) // look for more column markers
2496 {
2497 if (data[i]=='|' && (i==0 || data[i-1]!='\\')) columns++;
2498 if (columns==1) columns++; // first | make a non-table into a two column table
2499 i++;
2500 }
2501 }
2502 if (n==2 && columns==0) // table row has | ... |
2503 {
2504 columns++;
2505 }
2506 AUTO_TRACE_EXIT("eol={} start={} end={} columns={}",eol,start,end,columns);
2507 return eol;
2508}
#define eol
The end of line string for this machine.
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
size_t isNewline(std::string_view data)
Definition markdown.cpp:215

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

Referenced by isTableBlock(), and Markdown::Private::writeTableBlock().

◆ getFilteredImageAttributes()

QCString getFilteredImageAttributes ( std::string_view fmt,
const QCString & attrs )
static

parse the image attributes and return attributes for given format

Definition at line 331 of file markdown.cpp.

332{
333 AUTO_TRACE("fmt={} attrs={}",fmt,attrs);
334 StringVector attrList = split(attrs.str(),",");
335 for (const auto &attr_ : attrList)
336 {
337 QCString attr = QCString(attr_).stripWhiteSpace();
338 int i = attr.find(':');
339 if (i>0) // has format
340 {
341 QCString format = attr.left(i).stripWhiteSpace().lower();
342 if (format == fmt) // matching format
343 {
344 AUTO_TRACE_EXIT("result={}",attr.mid(i+1));
345 return attr.mid(i+1); // keep part after :
346 }
347 }
348 else // option that applies to all formats
349 {
350 AUTO_TRACE_EXIT("result={}",attr);
351 return attr;
352 }
353 }
354 return QCString();
355}
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
const std::string & str() const
Definition qcstring.h:552
QCString left(size_t len) const
Definition qcstring.h:229
std::vector< std::string > StringVector
Definition containers.h:33
Definition message.h:144
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6528

References AUTO_TRACE, AUTO_TRACE_EXIT, QCString::find(), QCString::left(), QCString::lower(), QCString::mid(), split(), QCString::str(), and QCString::stripWhiteSpace().

Referenced by Markdown::Private::writeMarkdownImage().

◆ hasLineBreak()

bool hasLineBreak ( std::string_view data)
static

Definition at line 2751 of file markdown.cpp.

2752{
2753 AUTO_TRACE("data='{}'",Trace::trunc(data));
2754 size_t i=0;
2755 size_t j=0;
2756 // search for end of line and also check if it is not a completely blank
2757 while (i<data.size() && data[i]!='\n')
2758 {
2759 if (data[i]!=' ' && data[i]!='\t') j++; // some non whitespace
2760 i++;
2761 }
2762 if (i>=data.size()) { return 0; } // empty line
2763 if (i<2) { return 0; } // not long enough
2764 bool res = (j>0 && data[i-1]==' ' && data[i-2]==' '); // non blank line with at two spaces at the end
2765 AUTO_TRACE_EXIT("result={}",res);
2766 return res;
2767}

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

Referenced by Markdown::Private::writeOneLineHeaderOrRuler().

◆ isBlockQuote()

bool isBlockQuote ( std::string_view data,
size_t indent )
static

returns true if this line starts a block quote

Definition at line 1914 of file markdown.cpp.

1915{
1916 AUTO_TRACE("data='{}' indent={}",Trace::trunc(data),indent);
1917 size_t i = 0;
1918 const size_t size = data.size();
1919 while (i<size && data[i]==' ') i++;
1920 if (i<indent+codeBlockIndent) // could be a quotation
1921 {
1922 // count >'s and skip spaces
1923 int level=0;
1924 while (i<size && (data[i]=='>' || data[i]==' '))
1925 {
1926 if (data[i]=='>') level++;
1927 i++;
1928 }
1929 // last characters should be a space or newline,
1930 // so a line starting with >= does not match, but only when level equals 1
1931 bool res = (level>0 && i<size && ((data[i-1]==' ') || data[i]=='\n')) || (level > 1);
1932 AUTO_TRACE_EXIT("result={}",res);
1933 return res;
1934 }
1935 else // too much indentation -> code block
1936 {
1937 AUTO_TRACE_EXIT("result=false: too much indentation");
1938 return false;
1939 }
1940}
const size_t codeBlockIndent
Definition markdown.cpp:209

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

Referenced by Markdown::Private::processQuotations().

◆ isCodeBlock()

bool isCodeBlock ( std::string_view data,
size_t offset,
size_t & indent )
static

Definition at line 2377 of file markdown.cpp.

2378{
2379 AUTO_TRACE("data='{}' offset={}",Trace::trunc(data),offset);
2380 //printf("<isCodeBlock(offset=%d,size=%d,indent=%d)\n",offset,size,indent);
2381 // determine the indent of this line
2382 size_t i=0;
2383 size_t indent0=0;
2384 const size_t size = data.size();
2385 while (i<size && data[i]==' ') indent0++,i++;
2386
2387 if (indent0<codeBlockIndent)
2388 {
2389 AUTO_TRACE_EXIT("result={}: line is not indented enough {}<4",false,indent0);
2390 return false;
2391 }
2392 if (indent0>=size || data[indent0]=='\n') // empty line does not start a code block
2393 {
2394 AUTO_TRACE_EXIT("result={}: only spaces at the end of a comment block",false);
2395 return false;
2396 }
2397
2398 i=offset;
2399 int nl=0;
2400 int nl_pos[3];
2401 int offset_i = static_cast<int>(offset);
2402 // search back 3 lines and remember the start of lines -1 and -2
2403 while (i>0 && nl<3) // i counts down from offset to 1
2404 {
2405 int j = static_cast<int>(i)-offset_i-1; // j counts from -1 to -offset
2406 // since j can be negative we need to rewrap data in a std::string_view
2407 size_t nl_size = isNewline(std::string_view(data.data()+j,data.size()-j));
2408 if (nl_size>0)
2409 {
2410 nl_pos[nl++]=j+static_cast<int>(nl_size);
2411 }
2412 i--;
2413 }
2414
2415 // if there are only 2 preceding lines, then line -2 starts at -offset
2416 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2417
2418 if (nl==3) // we have at least 2 preceding lines
2419 {
2420 //printf(" positions: nl_pos=[%d,%d,%d] line[-2]='%s' line[-1]='%s'\n",
2421 // nl_pos[0],nl_pos[1],nl_pos[2],
2422 // qPrint(QCString(data+nl_pos[1]).left(nl_pos[0]-nl_pos[1]-1)),
2423 // qPrint(QCString(data+nl_pos[2]).left(nl_pos[1]-nl_pos[2]-1)));
2424
2425 // check that line -1 is empty
2426 // Note that the offset is negative so we need to rewrap the string view
2427 if (!isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2428 {
2429 AUTO_TRACE_EXIT("result={}",FALSE);
2430 return FALSE;
2431 }
2432
2433 // determine the indent of line -2
2434 // Note that the offset is negative so we need to rewrap the string view
2435 indent=std::max(indent,computeIndentExcludingListMarkers(
2436 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2437
2438 //printf(">isCodeBlock local_indent %d>=%d+%d=%d\n",
2439 // indent0,indent,codeBlockIndent,indent0>=indent+codeBlockIndent);
2440 // if the difference is >4 spaces -> code block
2441 bool res = indent0>=indent+codeBlockIndent;
2442 AUTO_TRACE_EXIT("result={}: code block if indent difference >4 spaces",res);
2443 return res;
2444 }
2445 else // not enough lines to determine the relative indent, use global indent
2446 {
2447 // check that line -1 is empty
2448 // Note that the offset is negative so we need to rewrap the string view
2449 if (nl==1 && !isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2450 {
2451 AUTO_TRACE_EXIT("result=false");
2452 return FALSE;
2453 }
2454 //printf(">isCodeBlock global indent %d>=%d+4=%d nl=%d\n",
2455 // indent0,indent,indent0>=indent+4,nl);
2456 bool res = indent0>=indent+codeBlockIndent;
2457 AUTO_TRACE_EXIT("result={}: code block if indent difference >4 spaces",res);
2458 return res;
2459 }
2460}
static size_t computeIndentExcludingListMarkers(std::string_view data)
static bool isEmptyLine(std::string_view data)

References AUTO_TRACE, AUTO_TRACE_EXIT, codeBlockIndent, computeIndentExcludingListMarkers(), FALSE, isEmptyLine(), isNewline(), and Trace::trunc().

Referenced by Markdown::Private::processBlocks().

◆ isEmptyLine()

bool isEmptyLine ( std::string_view data)
static

Definition at line 2163 of file markdown.cpp.

2164{
2165 AUTO_TRACE("data='{}'",Trace::trunc(data));
2166 size_t i=0;
2167 while (i<data.size())
2168 {
2169 if (data[i]=='\n') { AUTO_TRACE_EXIT("true"); return true; }
2170 if (data[i]!=' ') { AUTO_TRACE_EXIT("false"); return false; }
2171 i++;
2172 }
2173 AUTO_TRACE_EXIT("true");
2174 return true;
2175}

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

Referenced by isCodeBlock(), Markdown::Private::processBlocks(), and Markdown::Private::processQuotations().

◆ isEndOfList()

bool isEndOfList ( std::string_view data)
static

Definition at line 2265 of file markdown.cpp.

2266{
2267 AUTO_TRACE("data='{}'",Trace::trunc(data));
2268 int dots=0;
2269 size_t i=0;
2270 // end of list marker is an otherwise empty line with a dot.
2271 while (i<data.size())
2272 {
2273 if (data[i]=='.')
2274 {
2275 dots++;
2276 }
2277 else if (data[i]=='\n')
2278 {
2279 break;
2280 }
2281 else if (data[i]!=' ' && data[i]!='\t') // bail out if the line is not empty
2282 {
2283 AUTO_TRACE_EXIT("result=false");
2284 return false;
2285 }
2286 i++;
2287 }
2288 AUTO_TRACE_EXIT("result={}",dots==1);
2289 return dots==1;
2290}

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

Referenced by Markdown::Private::processBlocks(), and Markdown::Private::processQuotations().

◆ isExplicitPage()

ExplicitPageResult isExplicitPage ( const QCString & docs)
static

Definition at line 3522 of file markdown.cpp.

3523{
3524 AUTO_TRACE("docs={}",Trace::trunc(docs));
3525 size_t i=0;
3526 std::string_view data(docs.str());
3527 const size_t size = data.size();
3528 if (!data.empty())
3529 {
3530 while (i<size && (data[i]==' ' || data[i]=='\n'))
3531 {
3532 i++;
3533 }
3534 if (literal_at(data.substr(i),"<!--!")) // skip over <!--! marker
3535 {
3536 i+=5;
3537 while (i<size && (data[i]==' ' || data[i]=='\n')) // skip over spaces after the <!--! marker
3538 {
3539 i++;
3540 }
3541 }
3542 if (i+1<size &&
3543 (data[i]=='\\' || data[i]=='@') &&
3544 (literal_at(data.substr(i+1),"page ") || literal_at(data.substr(i+1),"mainpage"))
3545 )
3546 {
3547 if (literal_at(data.substr(i+1),"page "))
3548 {
3549 AUTO_TRACE_EXIT("result=ExplicitPageResult::explicitPage");
3551 }
3552 else
3553 {
3554 AUTO_TRACE_EXIT("result=ExplicitPageResult::explicitMainPage");
3556 }
3557 }
3558 else if (i+1<size && (data[i]=='\\' || data[i]=='@') && isOtherPage(data.substr(i+1)))
3559 {
3560 AUTO_TRACE_EXIT("result=ExplicitPageResult::explicitOtherPage");
3562 }
3563 }
3564 AUTO_TRACE_EXIT("result=ExplicitPageResult::notExplicit");
3566}
static bool isOtherPage(std::string_view data)

References AUTO_TRACE, AUTO_TRACE_EXIT, explicitMainPage, explicitOtherPage, explicitPage, isOtherPage(), literal_at(), notExplicit, QCString::str(), and Trace::trunc().

Referenced by MarkdownOutlineParser::parseInput().

◆ isFencedCodeBlock()

bool isFencedCodeBlock ( std::string_view data,
size_t refIndent,
QCString & lang,
size_t & start,
size_t & end,
size_t & offset )
static

extract python or .py from python... or .py...

Definition at line 2292 of file markdown.cpp.

2294{
2295 AUTO_TRACE("data='{}' refIndent={}",Trace::trunc(data),refIndent);
2296 const char dot = '.';
2297 auto isAlphaChar = [ ](char c) { return (c>='A' && c<='Z') || (c>='a' && c<='z'); };
2298 auto isAlphaNChar = [ ](char c) { return (c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || (c=='+'); };
2299 auto isLangChar = [&](char c) { return c==dot || isAlphaChar(c); };
2300 // rules: at least 3 ~~~, end of the block same amount of ~~~'s, otherwise
2301 // return FALSE
2302 size_t i=0;
2303 size_t indent=0;
2304 int startTildes=0;
2305 const size_t size = data.size();
2306 while (i<size && data[i]==' ') indent++,i++;
2307 if (indent>=refIndent+4)
2308 {
2309 AUTO_TRACE_EXIT("result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2310 return FALSE;
2311 } // part of code block
2312 char tildaChar='~';
2313 if (i<size && data[i]=='`') tildaChar='`';
2314 while (i<size && data[i]==tildaChar) startTildes++,i++;
2315 if (startTildes<3)
2316 {
2317 AUTO_TRACE_EXIT("result=false: no fence marker found #tildes={}",startTildes);
2318 return FALSE;
2319 } // not enough tildes
2320 if (i<size && data[i]=='{') // extract .py from ```{.py} ... ```
2321 {
2322 i++; // skip over {
2323 if (data[i] == dot) i++; // skip over initial dot
2324 size_t startLang=i;
2325 while (i<size && (data[i]!='\n' && data[i]!='}')) i++; // find matching }
2326 if (i<size && data[i]=='}')
2327 {
2328 lang = data.substr(startLang,i-startLang);
2329 i++;
2330 }
2331 else // missing closing bracket, treat `{` as part of the content
2332 {
2333 i=startLang-1;
2334 lang="";
2335 }
2336 }
2337 else if (i<size && isLangChar(data[i])) /// extract python or .py from ```python...``` or ```.py...```
2338 {
2339 if (data[i] == dot) i++; // skip over initial dot
2340 size_t startLang=i;
2341 if (i<size && isAlphaChar(data[i])) //check first character of language specifier
2342 {
2343 i++;
2344 while (i<size && isAlphaNChar(data[i])) i++; // find end of language specifier
2345 }
2346 lang = data.substr(startLang,i-startLang);
2347 }
2348 else // no language specified
2349 {
2350 lang="";
2351 }
2352
2353 start=i;
2354 while (i<size)
2355 {
2356 if (data[i]==tildaChar)
2357 {
2358 end=i;
2359 int endTildes=0;
2360 while (i<size && data[i]==tildaChar) endTildes++,i++;
2361 while (i<size && data[i]==' ') i++;
2362 {
2363 if (endTildes==startTildes)
2364 {
2365 offset=i;
2366 AUTO_TRACE_EXIT("result=true: found end marker at offset {} lang='{}'",offset,lang);
2367 return true;
2368 }
2369 }
2370 }
2371 i++;
2372 }
2373 AUTO_TRACE_EXIT("result=false: no end marker found lang={}'",lang);
2374 return false;
2375}

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

Referenced by Markdown::Private::processBlocks(), and Markdown::Private::processQuotations().

◆ isHRuler()

bool isHRuler ( std::string_view data)
static

Definition at line 2032 of file markdown.cpp.

2033{
2034 AUTO_TRACE("data='{}'",Trace::trunc(data));
2035 size_t i=0;
2036 size_t size = data.size();
2037 if (size>0 && data[size-1]=='\n') size--; // ignore newline character
2038 while (i<size && data[i]==' ') i++;
2039 if (i>=size) { AUTO_TRACE_EXIT("result=false: empty line"); return false; } // empty line
2040 char c=data[i];
2041 if (c!='*' && c!='-' && c!='_')
2042 {
2043 AUTO_TRACE_EXIT("result=false: {} is not a hrule character",c);
2044 return false; // not a hrule character
2045 }
2046 int n=0;
2047 while (i<size)
2048 {
2049 if (data[i]==c)
2050 {
2051 n++; // count rule character
2052 }
2053 else if (data[i]!=' ')
2054 {
2055 AUTO_TRACE_EXIT("result=false: line contains non hruler characters");
2056 return false; // line contains non hruler characters
2057 }
2058 i++;
2059 }
2060 AUTO_TRACE_EXIT("result={}",n>=3);
2061 return n>=3; // at least 3 characters needed for a hruler
2062}

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

Referenced by Markdown::Private::writeBlockQuote(), and Markdown::Private::writeOneLineHeaderOrRuler().

◆ isLinkRef()

size_t isLinkRef ( std::string_view data,
QCString & refid,
QCString & link,
QCString & title )
static

returns end of the link ref if this is indeed a link reference.

Definition at line 1943 of file markdown.cpp.

1944{
1945 AUTO_TRACE("data='{}'",Trace::trunc(data));
1946 const size_t size = data.size();
1947 // format: start with [some text]:
1948 size_t i = 0;
1949 while (i<size && data[i]==' ') i++;
1950 if (i>=size || data[i]!='[') { return 0; }
1951 i++;
1952 size_t refIdStart=i;
1953 while (i<size && data[i]!='\n' && data[i]!=']') i++;
1954 if (i>=size || data[i]!=']') { return 0; }
1955 refid = data.substr(refIdStart,i-refIdStart);
1956 if (refid.isEmpty()) { return 0; }
1957 AUTO_TRACE_ADD("refid found {}",refid);
1958 //printf(" isLinkRef: found refid='%s'\n",qPrint(refid));
1959 i++;
1960 if (i>=size || data[i]!=':') { return 0; }
1961 i++;
1962
1963 // format: whitespace* \n? whitespace* (<url> | url)
1964 while (i<size && data[i]==' ') i++;
1965 if (i<size && data[i]=='\n')
1966 {
1967 i++;
1968 while (i<size && data[i]==' ') i++;
1969 }
1970 if (i>=size) { return 0; }
1971
1972 if (i<size && data[i]=='<') i++;
1973 size_t linkStart=i;
1974 while (i<size && data[i]!=' ' && data[i]!='\n') i++;
1975 size_t linkEnd=i;
1976 if (i<size && data[i]=='>') i++;
1977 if (linkStart==linkEnd) { return 0; } // empty link
1978 link = data.substr(linkStart,linkEnd-linkStart);
1979 AUTO_TRACE_ADD("link found {}",Trace::trunc(link));
1980 if (link=="@ref" || link=="\\ref")
1981 {
1982 size_t argStart=i;
1983 while (i<size && data[i]!='\n' && data[i]!='"') i++;
1984 link+=data.substr(argStart,i-argStart);
1985 }
1986
1987 title.clear();
1988
1989 // format: (whitespace* \n? whitespace* ( 'title' | "title" | (title) ))?
1990 size_t eol=0;
1991 while (i<size && data[i]==' ') i++;
1992 if (i<size && data[i]=='\n')
1993 {
1994 eol=i;
1995 i++;
1996 while (i<size && data[i]==' ') i++;
1997 }
1998 if (i>=size)
1999 {
2000 AUTO_TRACE_EXIT("result={}: end of isLinkRef while looking for title",i);
2001 return i; // end of buffer while looking for the optional title
2002 }
2003
2004 char c = data[i];
2005 if (c=='\'' || c=='"' || c=='(') // optional title present?
2006 {
2007 //printf(" start of title found! char='%c'\n",c);
2008 i++;
2009 if (c=='(') c=')'; // replace c by end character
2010 size_t titleStart=i;
2011 // search for end of the line
2012 while (i<size && data[i]!='\n') i++;
2013 eol = i;
2014
2015 // search back to matching character
2016 size_t end=i-1;
2017 while (end>titleStart && data[end]!=c) end--;
2018 if (end>titleStart)
2019 {
2020 title = data.substr(titleStart,end-titleStart);
2021 }
2022 AUTO_TRACE_ADD("title found {}",Trace::trunc(title));
2023 }
2024 while (i<size && data[i]==' ') i++;
2025 //printf("end of isLinkRef: i=%d size=%d data[i]='%c' eol=%d\n",
2026 // i,size,data[i],eol);
2027 if (i>=size) { AUTO_TRACE_EXIT("result={}",i); return i; } // end of buffer while ref id was found
2028 else if (eol>0) { AUTO_TRACE_EXIT("result={}",eol); return eol; } // end of line while ref id was found
2029 return 0; // invalid link ref
2030}
void clear()
Definition qcstring.h:182
#define AUTO_TRACE_ADD(...)
Definition markdown.cpp:62

References AUTO_TRACE, AUTO_TRACE_ADD, AUTO_TRACE_EXIT, QCString::clear(), end(), eol, QCString::isEmpty(), and Trace::trunc().

Referenced by Markdown::Private::processBlocks().

◆ isListMarker()

size_t isListMarker ( std::string_view data)
static

Definition at line 2254 of file markdown.cpp.

2255{
2256 AUTO_TRACE("data='{}'",Trace::trunc(data));
2257 size_t normalIndent = 0;
2258 while (normalIndent<data.size() && data[normalIndent]==' ') normalIndent++;
2259 size_t listIndent = computeIndentExcludingListMarkers(data);
2260 size_t result = listIndent>normalIndent ? listIndent : 0;
2261 AUTO_TRACE_EXIT("result={}",result);
2262 return result;
2263}

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

Referenced by Markdown::Private::processBlocks(), Markdown::Private::processQuotations(), and Markdown::Private::writeBlockQuote().

◆ isNewline()

size_t isNewline ( std::string_view data)
inline

Definition at line 215 of file markdown.cpp.

216{
217 // normal newline
218 if (data[0] == '\n') return 1;
219 // artificial new line from ^^ in ALIASES
220 if (literal_at(data,"\\ilinebr"))
221 {
222 return (data.size()>8 && data[8]==' ') ? 9 : 8; // also count space after \ilinebr if present
223 }
224 return 0;
225}

References literal_at().

Referenced by Markdown::Private::findEndOfLine(), findTableColumns(), and isCodeBlock().

◆ isOtherPage()

bool isOtherPage ( std::string_view data)
static

Definition at line 3509 of file markdown.cpp.

3510{
3511#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3512 OPC(dir); OPC(defgroup); OPC(addtogroup); OPC(weakgroup); OPC(ingroup);
3513 OPC(fn); OPC(property); OPC(typedef); OPC(var); OPC(def);
3514 OPC(enum); OPC(namespace); OPC(class); OPC(concept); OPC(module);
3515 OPC(protocol); OPC(category); OPC(union); OPC(struct); OPC(interface);
3516 OPC(idlexcept); OPC(file);
3517#undef OPC
3518
3519 return false;
3520}
#define OPC(x)

References OPC.

Referenced by isExplicitPage().

◆ isTableBlock()

bool isTableBlock ( std::string_view data)
static

Returns TRUE iff data points to the start of a table block.

Definition at line 2511 of file markdown.cpp.

2512{
2513 AUTO_TRACE("data='{}'",Trace::trunc(data));
2514 size_t cc0=0, start=0, end=0;
2515
2516 // the first line should have at least two columns separated by '|'
2517 size_t i = findTableColumns(data,start,end,cc0);
2518 if (i>=data.size() || cc0<1)
2519 {
2520 AUTO_TRACE_EXIT("result=false: no |'s in the header");
2521 return FALSE;
2522 }
2523
2524 size_t cc1 = 0;
2525 size_t ret = findTableColumns(data.substr(i),start,end,cc1);
2526 size_t j=i+start;
2527 // separator line should consist of |, - and : and spaces only
2528 while (j<=end+i)
2529 {
2530 if (data[j]!=':' && data[j]!='-' && data[j]!='|' && data[j]!=' ')
2531 {
2532 AUTO_TRACE_EXIT("result=false: invalid character '{}'",data[j]);
2533 return FALSE; // invalid characters in table separator
2534 }
2535 j++;
2536 }
2537 if (cc1!=cc0) // number of columns should be same as previous line
2538 {
2539 AUTO_TRACE_EXIT("result=false: different number of columns as previous line {}!={}",cc1,cc0);
2540 return FALSE;
2541 }
2542
2543 i+=ret; // goto next line
2544 size_t cc2 = 0;
2545 findTableColumns(data.substr(i),start,end,cc2);
2546
2547 AUTO_TRACE_EXIT("result={}",cc1==cc2);
2548 return cc1==cc2;
2549}
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, end(), FALSE, findTableColumns(), and Trace::trunc().

Referenced by Markdown::Private::processBlocks().

◆ markdownFileNameToId()

QCString markdownFileNameToId ( const QCString & fileName)

processes string s and converts markdown into doxygen/html commands.

Definition at line 3672 of file markdown.cpp.

3673{
3674 AUTO_TRACE("fileName={}",fileName);
3675 QCString absFileName = FileInfo(fileName.str()).absFilePath();
3676 QCString baseFn = stripFromPath(absFileName);
3677 int i = baseFn.findRev('.');
3678 if (i!=-1) baseFn = baseFn.left(i);
3679 QCString baseName = escapeCharsInString(baseFn,false,false);
3680 //printf("markdownFileNameToId(%s)=md_%s\n",qPrint(fileName),qPrint(baseName));
3681 QCString res = "md_"+baseName;
3682 AUTO_TRACE_EXIT("result={}",res);
3683 return res;
3684}
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
std::string absFilePath() const
Definition fileinfo.cpp:101
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
QCString escapeCharsInString(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3251
static QCString stripFromPath(const QCString &p, const StringVector &l)
Definition util.cpp:307

References FileInfo::absFilePath(), AUTO_TRACE, AUTO_TRACE_EXIT, escapeCharsInString(), QCString::findRev(), QCString::left(), QCString::str(), and stripFromPath().

Referenced by DocRef::DocRef(), DocSecRefItem::parse(), and MarkdownOutlineParser::parseInput().

◆ markersToAlignment()

Alignment markersToAlignment ( bool leftMarker,
bool rightMarker )
static

helper function to convert presence of left and/or right alignment markers to an alignment value

Definition at line 310 of file markdown.cpp.

311{
312 if (leftMarker && rightMarker)
313 {
314 return AlignCenter;
315 }
316 else if (leftMarker)
317 {
318 return AlignLeft;
319 }
320 else if (rightMarker)
321 {
322 return AlignRight;
323 }
324 else
325 {
326 return AlignNone;
327 }
328}

References AlignCenter, AlignLeft, AlignNone, and AlignRight.

Referenced by Markdown::Private::writeTableBlock().

◆ skipOverFileAndLineCommands()

bool skipOverFileAndLineCommands ( std::string_view data,
size_t indent,
size_t & offset,
std::string & location )

Definition at line 2948 of file markdown.cpp.

2949{
2950 size_t i = offset;
2951 size_t size = data.size();
2952 while (i<data.size() && data[i]==' ') i++;
2953 if (literal_at(data.substr(i),"\\ifile \""))
2954 {
2955 size_t locStart = i;
2956 if (i>offset) locStart--; // include the space before \ifile
2957 i+=8;
2958 bool found=false;
2959 while (i+9<size && data[i]!='\n')
2960 {
2961 if (literal_at(data.substr(i),"\\ilinebr "))
2962 {
2963 found=true;
2964 break;
2965 }
2966 i++;
2967 }
2968 if (found)
2969 {
2970 i+=9;
2971 location=data.substr(locStart,i-locStart);
2972 location+='\n';
2973 while (indent>0 && i<size && data[i]==' ') i++,indent--;
2974 if (i<size && data[i]=='\n') i++;
2975 offset = i;
2976 return true;
2977 }
2978 }
2979 return false;
2980}

References literal_at().

Referenced by Markdown::Private::writeCodeBlock().

Variable Documentation

◆ codeBlockIndent

const size_t codeBlockIndent = 4

◆ g_doxy_nbsp

const char* g_doxy_nbsp = "&_doxy_nbsp;"

Definition at line 208 of file markdown.cpp.

Referenced by Markdown::Private::addStrEscapeUtf8Nbsp(), and Markdown::process().

◆ g_quotationHeaderMap

const std::unordered_map<std::string,std::string> g_quotationHeaderMap
static
Initial value:
= {
{ "[!note]", "\\note" },
{ "[!warning]", "\\warning" },
{ "[!tip]", "\\remark" },
{ "[!caution]", "\\attention" },
{ "[!important]", "\\important" }
}

Definition at line 2821 of file markdown.cpp.

2821 {
2822 // GitHub style Doxygen command
2823 { "[!note]", "\\note" },
2824 { "[!warning]", "\\warning" },
2825 { "[!tip]", "\\remark" },
2826 { "[!caution]", "\\attention" },
2827 { "[!important]", "\\important" }
2828};

Referenced by Markdown::Private::writeBlockQuote().

◆ g_utf8_nbsp

const char* g_utf8_nbsp = "\xc2\xa0"

Definition at line 207 of file markdown.cpp.

Referenced by Markdown::Private::addStrEscapeUtf8Nbsp().