diff options
Diffstat (limited to 'src/markdown.cpp')
-rw-r--r-- | src/markdown.cpp | 130 |
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," "); + QCString result = substitute(m_out.get(),g_doxy_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 } } |