summaryrefslogtreecommitdiff
path: root/src/markdown.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/markdown.cpp')
-rw-r--r--src/markdown.cpp130
1 files changed, 81 insertions, 49 deletions
diff --git a/src/markdown.cpp b/src/markdown.cpp
index c8e6723..350863b 100644
--- a/src/markdown.cpp
+++ b/src/markdown.cpp
@@ -105,7 +105,7 @@ class Trace
}
}
data_s[j++]=0;
- fprintf(IOSTREAM,"> %s data=[%s…]\n",qPrint(func),data_s);
+ fprintf(IOSTREAM,"> %s data=[%s...]\n",qPrint(func),data_s);
s_indent++;
}
}
@@ -187,7 +187,7 @@ int Trace::s_indent = 0;
((data[i]>='a' && data[i]<='z') || \
(data[i]>='A' && data[i]<='Z') || \
(data[i]>='0' && data[i]<='9') || \
- (((unsigned char)data[i])>=0x80)) // unicode characters
+ (static_cast<unsigned char>(data[i])>=0x80)) // unicode characters
#define extraChar(i) \
(data[i]=='-' || data[i]=='+' || data[i]=='!' || \
@@ -220,17 +220,17 @@ Markdown::Markdown(const QCString &fileName,int lineNr,int indentLevel)
{
using namespace std::placeholders;
// setup callback table for special characters
- m_actions[(unsigned int)'_'] = std::bind(&Markdown::processEmphasis, this,_1,_2,_3);
- m_actions[(unsigned int)'*'] = std::bind(&Markdown::processEmphasis, this,_1,_2,_3);
- m_actions[(unsigned int)'~'] = std::bind(&Markdown::processEmphasis, this,_1,_2,_3);
- m_actions[(unsigned int)'`'] = std::bind(&Markdown::processCodeSpan, this,_1,_2,_3);
- m_actions[(unsigned int)'\\']= std::bind(&Markdown::processSpecialCommand,this,_1,_2,_3);
- m_actions[(unsigned int)'@'] = std::bind(&Markdown::processSpecialCommand,this,_1,_2,_3);
- m_actions[(unsigned int)'['] = std::bind(&Markdown::processLink, this,_1,_2,_3);
- m_actions[(unsigned int)'!'] = std::bind(&Markdown::processLink, this,_1,_2,_3);
- m_actions[(unsigned int)'<'] = std::bind(&Markdown::processHtmlTag, this,_1,_2,_3);
- m_actions[(unsigned int)'-'] = std::bind(&Markdown::processNmdash, this,_1,_2,_3);
- m_actions[(unsigned int)'"'] = std::bind(&Markdown::processQuoted, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('_')] = std::bind(&Markdown::processEmphasis, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('*')] = std::bind(&Markdown::processEmphasis, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('~')] = std::bind(&Markdown::processEmphasis, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('`')] = std::bind(&Markdown::processCodeSpan, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('\\')]= std::bind(&Markdown::processSpecialCommand,this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('@')] = std::bind(&Markdown::processSpecialCommand,this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('[')] = std::bind(&Markdown::processLink, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('!')] = std::bind(&Markdown::processLink, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('<')] = std::bind(&Markdown::processHtmlTag, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('-')] = std::bind(&Markdown::processNmdash, this,_1,_2,_3);
+ m_actions[static_cast<unsigned int>('"')] = std::bind(&Markdown::processQuoted, this,_1,_2,_3);
(void)m_lineNr; // not used yet
}
@@ -239,8 +239,8 @@ enum Alignment { AlignNone, AlignLeft, AlignCenter, AlignRight };
//---------- constants -------
//
-const uchar g_utf8_nbsp[3] = { 0xc2, 0xa0, 0}; // UTF-8 nbsp
-const char *g_doxy_nsbp = "&_doxy_nbsp;"; // doxygen escape command for UTF-8 nbsp
+const char *g_utf8_nbsp = "\xc2\xa0"; // UTF-8 nbsp
+const char *g_doxy_nbsp = "&_doxy_nbsp;"; // doxygen escape command for UTF-8 nbsp
const int codeBlockIndent = 4;
//---------- helpers -------
@@ -324,7 +324,7 @@ static void convertStringFragment(QCString &result,const char *data,int size)
{
TRACE(result);
if (size<0) size=0;
- result = QCString(data,(uint)size);
+ result = QCString(data,static_cast<size_t>(size));
TRACE_RESULT(result);
}
@@ -492,7 +492,7 @@ int Markdown::isSpecialCommand(const char *data,int offset,int size)
// skip over spaces
while (offset_<size_ && data_[offset_]==' ') offset_++;
// skip over label
- while (offset_<size_ && (c=data_[offset_])!=' ' && c!='\n') offset_++;
+ while (offset_<size_ && (c=data_[offset_])!=' ' && c!='\\' && c!='@' && c!='\n') offset_++;
return offset_;
}
return 0;
@@ -517,16 +517,22 @@ int Markdown::isSpecialCommand(const char *data,int offset,int size)
return endOfLabel(data_,offset_,size_);
};
- static const auto endOfFunc = [](const char *data_,int offset_,int size_) -> int
+ static const auto endOfFuncLike = [](const char *data_,int offset_,int size_,bool allowSpaces) -> int
{
if (offset_<size_ && data_[offset_]==' ') // we expect a space before the name
{
char c=0;
offset_++;
// skip over spaces
- while (offset_<size_ && data_[offset_]==' ') offset_++;
- // skip over name
- while (offset_<size_ && (c=data_[offset_])!=' ' && c!='\n' && c!='(') offset_++;
+ while (offset_<size_ && data_[offset_]==' ')
+ {
+ offset_++;
+ }
+ // skip over name (and optionally type)
+ while (offset_<size_ && (c=data_[offset_])!='\n' && (allowSpaces || c!=' ') && c!='(')
+ {
+ offset_++;
+ }
if (c=='(') // find the end of the function
{
int count=1;
@@ -543,9 +549,14 @@ int Markdown::isSpecialCommand(const char *data,int offset,int size)
return 0;
};
+ static const auto endOfFunc = [](const char *data_,int offset_,int size_) -> int
+ {
+ return endOfFuncLike(data_,offset_,size_,true);
+ };
+
static const auto endOfGuard = [](const char *data_,int offset_,int size_) -> int
{
- return endOfFunc(data_,offset_,size_);
+ return endOfFuncLike(data_,offset_,size_,false);
};
static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
@@ -609,7 +620,7 @@ int Markdown::isSpecialCommand(const char *data,int offset,int size)
{ "property", endOfLine },
{ "protocol", endOfLine },
{ "ref", endOfLabel },
- { "refitem", endOfLabel },
+ { "refitem", endOfLine },
{ "related", endOfLabel },
{ "relatedalso", endOfLabel },
{ "relates", endOfLabel },
@@ -1402,7 +1413,6 @@ int Markdown::processLink(const char *data,int offset,int size)
return 0;
}
nlTotal += nl;
- nl = 0;
// search for optional image attributes
QCString attributes;
@@ -1441,7 +1451,6 @@ int Markdown::processLink(const char *data,int offset,int size)
i++;
}
nlTotal += nl;
- nl = 0;
if (i>=size) return 0; // premature end of comment -> no attributes
int attributesEnd=i;
convertStringFragment(attributes,data+attributesStart,attributesEnd-attributesStart);
@@ -1489,6 +1498,7 @@ int Markdown::processLink(const char *data,int offset,int size)
writeMarkdownImage("latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
writeMarkdownImage("rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
writeMarkdownImage("docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
+ writeMarkdownImage("xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
}
else
{
@@ -1526,7 +1536,7 @@ int Markdown::processLink(const char *data,int offset,int size)
else if (!(forg.exists() && forg.isReadable()))
{
FileInfo fi(m_fileName.str());
- QCString mdFile = m_fileName.left(m_fileName.length()-(uint)fi.fileName().length()) + link;
+ QCString mdFile = m_fileName.left(m_fileName.length()-fi.fileName().length()) + link;
FileInfo fmd(mdFile.str());
if (fmd.exists() && fmd.isReadable())
{
@@ -1671,13 +1681,13 @@ int Markdown::processCodeSpan(const char *data, int /*offset*/, int size)
void Markdown::addStrEscapeUtf8Nbsp(const char *s,int len)
{
TRACE(s);
- if (Portable::strnstr(s,g_doxy_nsbp,len)==0) // no escape needed -> fast
+ if (Portable::strnstr(s,g_doxy_nbsp,len)==0) // no escape needed -> fast
{
m_out.addStr(s,len);
}
else // escape needed -> slow
{
- m_out.addStr(substitute(QCString(s).left(len),g_doxy_nsbp,(const char *)g_utf8_nbsp));
+ m_out.addStr(substitute(QCString(s).left(len),g_doxy_nbsp,g_utf8_nbsp));
}
}
@@ -1746,7 +1756,7 @@ void Markdown::processInline(const char *data,int size)
while (i<size)
{
// skip over character that do not trigger a specific action
- while (end<size && ((action=m_actions[(uchar)data[end]])==0)) end++;
+ while (end<size && ((action=m_actions[static_cast<uchar>(data[end])])==0)) end++;
// and add them to the output
m_out.addStr(data+i,end-i);
if (end>=size) break;
@@ -1964,7 +1974,7 @@ static QCString extractTitleId(QCString &title, int level)
if (reg::search(ti,match,r2))
{
std::string id = match[1].str();
- title = title.left((int)match.position());
+ title = title.left(match.position());
//printf("found match id='%s' title=%s\n",id.c_str(),qPrint(title));
TRACE_RESULT(QCString(id));
return QCString(id);
@@ -2958,7 +2968,38 @@ QCString Markdown::processQuotations(const QCString &s,int refIndent)
{
if (isFencedCodeBlock(data+pi,size-pi,currentIndent,lang,blockStart,blockEnd,blockOffset))
{
- writeFencedCodeBlock(data+pi,lang.data(),blockStart,blockEnd);
+ auto addSpecialCommand = [&](const QCString &startCmd,const QCString &endCmd)
+ {
+ int cmdPos = pi+blockStart+1;
+ QCString pl = QCString(data+cmdPos).left(blockEnd-blockStart-1);
+ uint ii = 0;
+ // check for absence of start command, either @start<cmd>, or \\start<cmd>
+ while (ii<pl.length() && qisspace(pl[ii])) ii++; // skip leading whitespace
+ if (ii+startCmd.length()>=pl.length() || // no room for start command
+ (pl[ii]!='\\' && pl[ii]!='@') || // no @ or \ after whitespace
+ qstrncmp(pl.data()+ii+1,startCmd.data(),startCmd.length())!=0) // no start command
+ {
+ pl = "@"+startCmd+"\\ilinebr " + pl + " @"+endCmd;
+ }
+ processSpecialCommand(pl.data(),0,pl.length());
+ };
+
+ if (!Config_getString(PLANTUML_JAR_PATH).isEmpty() && lang=="plantuml")
+ {
+ addSpecialCommand("startuml","enduml");
+ }
+ else if (Config_getBool(HAVE_DOT) && lang=="dot")
+ {
+ addSpecialCommand("dot","enddot");
+ }
+ else if (lang=="msc") // msc is built-in
+ {
+ addSpecialCommand("msc","endmsc");
+ }
+ else // normal code block
+ {
+ writeFencedCodeBlock(data+pi,lang.data(),blockStart,blockEnd);
+ }
i=pi+blockOffset;
pi=-1;
end=i+1;
@@ -3007,7 +3048,6 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
int size = s.length();
int i=0,end=0,pi=-1,ref,level;
QCString id,link,title;
- int blockIndent = indent;
// get indent for the first line
end = i+1;
@@ -3052,7 +3092,6 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
{
//printf("** end of list\n");
currentIndent = indent;
- blockIndent = indent;
insideList = false;
}
newBlock = false;
@@ -3065,7 +3104,6 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
//printf("** start of list\n");
insideList = true;
currentIndent = listIndent;
- blockIndent = listIndent;
}
}
else if (isEndOfList(data+i,end-i))
@@ -3073,7 +3111,6 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
//printf("** end of list\n");
insideList = false;
currentIndent = listIndent;
- blockIndent = listIndent;
}
else if (isEmptyLine(data+i,end-i))
{
@@ -3089,7 +3126,7 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
{
int blockStart,blockEnd,blockOffset;
QCString lang;
- blockIndent = currentIndent;
+ int blockIndent = currentIndent;
//printf("isHeaderLine(%s)=%d\n",QCString(data+i).left(size-i).data(),level);
QCString endBlockName;
if (data[i]=='@' || data[i]=='\\') endBlockName = isBlockCommand(data+i,i,size-i);
@@ -3116,7 +3153,6 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
{
m_out.addChar(data[i]);
m_out.addStr(endBlockName);
- pi=i;
i+=l+1;
break;
}
@@ -3165,7 +3201,6 @@ QCString Markdown::processBlocks(const QCString &s,const int indent)
// qPrint(id),qPrint(link),qPrint(title));
m_linkRefs.insert({id.lower().str(),LinkRef(link,title)});
i=ref+pi;
- pi=-1;
end=i+1;
}
else if (isFencedCodeBlock(data+pi,size-pi,currentIndent,lang,blockStart,blockEnd,blockOffset))
@@ -3319,7 +3354,7 @@ QCString Markdown::detab(const QCString &s,int &refIndent)
int minIndent=maxIndent;
while (i<size)
{
- signed char c = (signed char)data[i++];
+ char c = data[i++];
switch(c)
{
case '\t': // expand tab
@@ -3345,7 +3380,7 @@ QCString Markdown::detab(const QCString &s,int &refIndent)
int nb = isUTF8NonBreakableSpace(data);
if (nb>0)
{
- m_out.addStr(g_doxy_nsbp);
+ m_out.addStr(g_doxy_nbsp);
i+=nb-1;
}
else
@@ -3408,7 +3443,7 @@ QCString Markdown::process(const QCString &input, int &startNewlines, bool fromP
}
// post processing
- QCString result = substitute(m_out.get(),g_doxy_nsbp,"&nbsp;");
+ QCString result = substitute(m_out.get(),g_doxy_nbsp,"&nbsp;");
const char *p = result.data();
if (p)
{
@@ -3514,21 +3549,18 @@ void MarkdownOutlineParser::parseInput(const QCString &fileName,
break;
case ExplicitPageResult::explicitPage:
{
- // look for `@page label Title\n` and capture `label`
- static const reg::Ex re(R"([\\@]page\s+(\a[\w-]*)\s*[^\n]*\n)");
+ // look for `@page label My Title\n` and capture `label` (match[1]) and ` My Title` (match[2])
+ static const reg::Ex re(R"([\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
reg::Match match;
std::string s = docs.str();
if (reg::search(s,match,re))
{
QCString orgLabel = match[1].str();
QCString newLabel = markdownFileNameToId(fileName);
- size_t labelStartPos = match[1].position();
- size_t labelEndPos = labelStartPos+match[1].length();
- size_t lineLen = match.length();
- docs = docs.left(labelStartPos)+ // part before label
+ docs = docs.left(match[1].position())+ // part before label
newLabel+ // new label
- docs.mid(labelEndPos,lineLen-labelEndPos-1)+ // part between orgLabel and \n
- "\\ilinebr @anchor "+orgLabel+"\n"+ // add original anchor
+ match[2].str()+ // part between orgLabel and \n
+ "\\ilinebr @anchor "+orgLabel+"\n"+ // add original anchor plus \n of above
docs.right(docs.length()-match.length()); // add remainder of docs
}
}