Commit 1941e88e authored by Dimitri van Heesch's avatar Dimitri van Heesch

Bug 691315 - Line breaks are not copied/displayed properly when using @copydoc.

Bug 700788 - probably unintentional modification of behaviour of @copydetails
parent ae845dea
...@@ -116,6 +116,8 @@ static bool handleToc(const QCString &s); ...@@ -116,6 +116,8 @@ static bool handleToc(const QCString &s);
static bool handleInherit(const QCString &); static bool handleInherit(const QCString &);
static bool handleExtends(const QCString &); static bool handleExtends(const QCString &);
static bool handleCopyDoc(const QCString &); static bool handleCopyDoc(const QCString &);
static bool handleCopyBrief(const QCString &);
static bool handleCopyDetails(const QCString &);
typedef bool (*DocCmdFunc)(const QCString &name); typedef bool (*DocCmdFunc)(const QCString &name);
...@@ -218,8 +220,8 @@ static DocCmdMap docCmdMap[] = ...@@ -218,8 +220,8 @@ static DocCmdMap docCmdMap[] =
{ "author", 0, TRUE }, { "author", 0, TRUE },
{ "authors", 0, TRUE }, { "authors", 0, TRUE },
{ "copydoc", &handleCopyDoc, TRUE }, { "copydoc", &handleCopyDoc, TRUE },
{ "copybrief", 0, FALSE }, { "copybrief", &handleCopyBrief, FALSE },
{ "copydetails", 0, TRUE }, { "copydetails", &handleCopyDetails, TRUE },
{ "copyright", 0, TRUE }, { "copyright", 0, TRUE },
{ "date", 0, TRUE }, { "date", 0, TRUE },
{ "dotfile", 0, TRUE }, { "dotfile", 0, TRUE },
...@@ -2674,9 +2676,43 @@ static bool handleExtends(const QCString &) ...@@ -2674,9 +2676,43 @@ static bool handleExtends(const QCString &)
return FALSE; return FALSE;
} }
static bool handleCopyBrief(const QCString &)
{
if (current->brief.isEmpty() && current->doc.isEmpty())
{ // if we don't have a brief or detailed description yet,
// then the @copybrief should end up in the brief description.
// otherwise it will be copied inline (see bug691315 & bug700788)
setOutput(OutputBrief);
}
if (g_spaceBeforeCmd)
{
addOutput(' ');
g_spaceBeforeCmd=FALSE;
}
addOutput("\\copybrief ");
return FALSE;
}
static bool handleCopyDetails(const QCString &)
{
setOutput(OutputDoc);
if (g_spaceBeforeCmd)
{
addOutput(' ');
g_spaceBeforeCmd=FALSE;
}
addOutput("\\copydetails ");
return FALSE;
}
static bool handleCopyDoc(const QCString &) static bool handleCopyDoc(const QCString &)
{ {
setOutput(OutputBrief); setOutput(OutputBrief);
if (g_spaceBeforeCmd)
{
addOutput(' ');
g_spaceBeforeCmd=FALSE;
}
addOutput("\\copybrief "); addOutput("\\copybrief ");
g_copyDocArg.resize(0); g_copyDocArg.resize(0);
BEGIN(CopyDoc); BEGIN(CopyDoc);
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include "reflist.h" #include "reflist.h"
#include "formula.h" #include "formula.h"
#include "config.h" #include "config.h"
#include "growbuf.h"
// debug off // debug off
#define DBG(x) do {} while(0) #define DBG(x) do {} while(0)
...@@ -1528,39 +1529,6 @@ handlepara: ...@@ -1528,39 +1529,6 @@ handlepara:
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static int handleDocCopy(DocNode *parent,QList<DocNode> &children)
{
int tok=doctokenizerYYlex();
int cmdId = Mappers::cmdMapper->map(g_token->name);
if (tok!=TK_WHITESPACE)
{
warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
qPrint(g_token->name));
return 0;
}
tok=doctokenizerYYlex();
if (tok==0)
{
warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
"argument of command %s\n", qPrint(g_token->name));
return 0;
}
else if (tok!=TK_WORD && tok!=TK_LNKWORD)
{
warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
tokToString(tok),qPrint(g_token->name));
return 0;
}
DocCopy *cpy = new DocCopy(parent,g_token->name,
cmdId==CMD_COPYDOC || cmdId==CMD_COPYBRIEF,
cmdId==CMD_COPYDOC || cmdId==CMD_COPYDETAILS);
cpy->parse(children);
delete cpy;
return TK_NEWPARA;
}
//---------------------------------------------------------------------------
static void handleImg(DocNode *parent,QList<DocNode> &children,const HtmlAttribList &tagHtmlAttribs) static void handleImg(DocNode *parent,QList<DocNode> &children,const HtmlAttribList &tagHtmlAttribs)
{ {
HtmlAttribListIterator li(tagHtmlAttribs); HtmlAttribListIterator li(tagHtmlAttribs);
...@@ -1757,10 +1725,6 @@ static int internalValidatingParseDoc(DocNode *parent,QList<DocNode> &children, ...@@ -1757,10 +1725,6 @@ static int internalValidatingParseDoc(DocNode *parent,QList<DocNode> &children,
{ {
delete par; delete par;
} }
if (retval==RetVal_CopyDoc)
{
retval=handleDocCopy(parent,children);
}
} while (retval==TK_NEWPARA); } while (retval==TK_NEWPARA);
if (lastPar) lastPar->markLast(); if (lastPar) lastPar->markLast();
...@@ -3239,10 +3203,6 @@ int DocInternal::parse(int level) ...@@ -3239,10 +3203,6 @@ int DocInternal::parse(int level)
{ {
warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found",doctokenizerYYlineno); warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found",doctokenizerYYlineno);
} }
else if (retval==RetVal_CopyDoc)
{
retval=handleDocCopy(this,m_children);
}
} while (retval!=0 && } while (retval!=0 &&
retval!=RetVal_Section && retval!=RetVal_Section &&
retval!=RetVal_Subsection && retval!=RetVal_Subsection &&
...@@ -5597,7 +5557,8 @@ int DocPara::handleCommand(const QCString &cmdName) ...@@ -5597,7 +5557,8 @@ int DocPara::handleCommand(const QCString &cmdName)
case CMD_COPYDOC: // fall through case CMD_COPYDOC: // fall through
case CMD_COPYBRIEF: // fall through case CMD_COPYBRIEF: // fall through
case CMD_COPYDETAILS: case CMD_COPYDETAILS:
retval = RetVal_CopyDoc; //retval = RetVal_CopyDoc;
// these commands should already be resolved by processCopyDoc()
break; break;
case CMD_INCLUDE: case CMD_INCLUDE:
handleInclude(cmdName,DocInclude::Include); handleInclude(cmdName,DocInclude::Include);
...@@ -6648,10 +6609,6 @@ int DocSection::parse() ...@@ -6648,10 +6609,6 @@ int DocSection::parse()
{ {
warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found"); warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found");
} }
else if (retval==RetVal_CopyDoc)
{
retval=handleDocCopy(this,m_children);
}
} while (retval!=0 && } while (retval!=0 &&
retval!=RetVal_Internal && retval!=RetVal_Internal &&
retval!=RetVal_Section && retval!=RetVal_Section &&
...@@ -6868,10 +6825,6 @@ void DocRoot::parse() ...@@ -6868,10 +6825,6 @@ void DocRoot::parse()
{ {
warn_doc_error(g_fileName,doctokenizerYYlineno,"found paragraph command outside of subsubsection context!"); warn_doc_error(g_fileName,doctokenizerYYlineno,"found paragraph command outside of subsubsection context!");
} }
else if (retval==RetVal_CopyDoc)
{
retval=handleDocCopy(this,m_children);
}
} while (retval!=0 && retval!=RetVal_Section && retval!=RetVal_Internal); } while (retval!=0 && retval!=RetVal_Section && retval!=RetVal_Internal);
if (lastPar) lastPar->markLast(); if (lastPar) lastPar->markLast();
...@@ -6909,6 +6862,140 @@ void DocRoot::parse() ...@@ -6909,6 +6862,140 @@ void DocRoot::parse()
DBG(("DocRoot::parse() end\n")); DBG(("DocRoot::parse() end\n"));
} }
static QCString extractCopyDocId(const char *data, uint &j, uint len)
{
uint s=j;
uint e=j;
int round=0;
bool insideDQuote=FALSE;
bool insideSQuote=FALSE;
bool found=FALSE;
while (j<len && !found)
{
if (!insideSQuote && !insideDQuote)
{
switch (data[j])
{
case '(': round++; break;
case ')': round--; break;
case '"': insideDQuote=TRUE; break;
case '\'': insideSQuote=TRUE; break;
case ' ': // fall through
case '\t': // fall through
case '\n':
found=(round==0);
break;
}
}
else if (insideSQuote) // look for single quote end
{
if (data[j]=='\'' && (j==0 || data[j]!='\\'))
{
insideSQuote=FALSE;
}
}
else if (insideDQuote) // look for double quote end
{
if (data[j]=='"' && (j==0 || data[j]!='\\'))
{
insideDQuote=FALSE;
}
}
if (!found) j++;
}
e=j;
QCString id(e-s+1);
if (e>s) memcpy(id.data(),data+s,e-s);
id.at(e-s)='\0';
//printf("extractCopyDocId=%s input='%s'\n",id.data(),&data[s]);
return id;
}
static uint isCopyBriefOrDetailsCmd(const char *data, uint i,uint len,bool &brief)
{
int j=0;
if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
{
if (i+10<len && qstrncmp(data+i+1,"copybrief",9)==0) // @copybrief or \copybrief
{
j=i+10;
brief=TRUE;
}
else if (i+12<len && qstrncmp(data+i+1,"copydetails",11)==0) // @copydetails or \copydetails
{
j=i+12;
brief=FALSE;
}
}
return j;
}
static QCString processCopyDoc(const char *data,uint &len)
{
//printf("processCopyDoc start '%s'\n",data);
GrowBuf buf;
uint i=0;
while (i<len)
{
char c = data[i];
if (c=='@' || c=='\\') // look for a command
{
bool isBrief=TRUE;
uint j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
if (j>0)
{
// skip whitespace
while (j<len && (data[j]==' ' || data[j]=='\t')) j++;
// extract the argument
QCString id = extractCopyDocId(data,j,len);
Definition *def;
QCString doc,brief;
//printf("resolving docs='%s'\n",id.data());
if (findDocsForMemberOrCompound(id,&doc,&brief,&def))
{
//printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,brief.data(),doc.data(),isBrief);
if (g_copyStack.findRef(def)==-1) // definition not parsed earlier
{
g_copyStack.append(def);
if (isBrief)
{
uint l=brief.length();
buf.addStr(processCopyDoc(brief,l));
}
else
{
uint l=doc.length();
buf.addStr(processCopyDoc(doc,l));
}
g_copyStack.remove(def);
}
else
{
warn_doc_error(g_fileName,doctokenizerYYlineno,
"Found recursive @copy%s or @copydoc relation for argument '%s'.\n",
isBrief?"brief":"details",id.data());
}
}
// skip over command
i=j;
}
else
{
buf.addChar(c);
i++;
}
}
else // not a command, just copy
{
buf.addChar(c);
i++;
}
}
len = buf.getPos();
buf.addChar(0);
return buf.get();
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
DocRoot *validatingParseDoc(const char *fileName,int startLine, DocRoot *validatingParseDoc(const char *fileName,int startLine,
...@@ -7074,12 +7161,13 @@ DocRoot *validatingParseDoc(const char *fileName,int startLine, ...@@ -7074,12 +7161,13 @@ DocRoot *validatingParseDoc(const char *fileName,int startLine,
//printf("Starting comment block at %s:%d\n",g_fileName.data(),startLine); //printf("Starting comment block at %s:%d\n",g_fileName.data(),startLine);
doctokenizerYYlineno=startLine; doctokenizerYYlineno=startLine;
QCString inpStr=input; uint inpLen=qstrlen(input);
uint inpLen = inpStr.length(); QCString inpStr = processCopyDoc(input,inpLen);
if (inpLen>0 && inpStr.at(inpLen-1)!='\n') if (inpStr.isEmpty() || inpStr.at(inpStr.length()-1)!='\n')
{ {
inpStr+='\n'; inpStr+='\n';
} }
//printf("processCopyDoc(in='%s' out='%s')\n",input,inpStr.data());
doctokenizerYYinit(inpStr,g_fileName); doctokenizerYYinit(inpStr,g_fileName);
// build abstract syntax tree // build abstract syntax tree
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment