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

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

Bug 700788 - probably unintentional modification of behaviour of @copydetails
parent ae845dea
Loading
Loading
Loading
Loading
+38 −2
Original line number Diff line number Diff line
@@ -116,6 +116,8 @@ static bool handleToc(const QCString &s);
static bool handleInherit(const QCString &);
static bool handleExtends(const QCString &);
static bool handleCopyDoc(const QCString &);
static bool handleCopyBrief(const QCString &);
static bool handleCopyDetails(const QCString &);

typedef bool (*DocCmdFunc)(const QCString &name);

@@ -218,8 +220,8 @@ static DocCmdMap docCmdMap[] =
  { "author",          0,                       TRUE  },
  { "authors",         0,                       TRUE  },
  { "copydoc",         &handleCopyDoc,          TRUE  },
  { "copybrief",       0,                       FALSE },
  { "copydetails",     0,                       TRUE  },
  { "copybrief",       &handleCopyBrief,        FALSE },
  { "copydetails",     &handleCopyDetails,      TRUE  },
  { "copyright",       0,                       TRUE  },
  { "date",            0,                       TRUE  },
  { "dotfile",         0,                       TRUE  },
@@ -2674,9 +2676,43 @@ static bool handleExtends(const QCString &)
  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 &)
{
  setOutput(OutputBrief);
  if (g_spaceBeforeCmd)
  {
    addOutput(' ');
    g_spaceBeforeCmd=FALSE;
  }
  addOutput("\\copybrief ");
  g_copyDocArg.resize(0);
  BEGIN(CopyDoc);
+141 −53
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@
#include "reflist.h"
#include "formula.h"
#include "config.h"
#include "growbuf.h"

// debug off
#define DBG(x) do {} while(0)
@@ -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)
{
  HtmlAttribListIterator li(tagHtmlAttribs);
@@ -1757,10 +1725,6 @@ static int internalValidatingParseDoc(DocNode *parent,QList<DocNode> &children,
    {
      delete par;
    }
    if (retval==RetVal_CopyDoc)
    {
      retval=handleDocCopy(parent,children);
    }
  } while (retval==TK_NEWPARA);
  if (lastPar) lastPar->markLast();

@@ -3239,10 +3203,6 @@ int DocInternal::parse(int level)
    {
      warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found",doctokenizerYYlineno);
    }
    else if (retval==RetVal_CopyDoc)
    {
      retval=handleDocCopy(this,m_children);
    }
  } while (retval!=0 && 
           retval!=RetVal_Section &&
           retval!=RetVal_Subsection &&
@@ -5597,7 +5557,8 @@ int DocPara::handleCommand(const QCString &cmdName)
    case CMD_COPYDOC:   // fall through
    case CMD_COPYBRIEF: // fall through
    case CMD_COPYDETAILS:
      retval = RetVal_CopyDoc;
      //retval = RetVal_CopyDoc;
      // these commands should already be resolved by processCopyDoc()
      break;
    case CMD_INCLUDE:
      handleInclude(cmdName,DocInclude::Include);
@@ -6648,10 +6609,6 @@ int DocSection::parse()
    {
      warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found");
    }
    else if (retval==RetVal_CopyDoc)
    {
      retval=handleDocCopy(this,m_children);
    }
  } while (retval!=0 && 
           retval!=RetVal_Internal      &&
           retval!=RetVal_Section       &&
@@ -6868,10 +6825,6 @@ void DocRoot::parse()
    {
      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);
  if (lastPar) lastPar->markLast();

@@ -6909,6 +6862,140 @@ void DocRoot::parse()
  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,
@@ -7074,12 +7161,13 @@ DocRoot *validatingParseDoc(const char *fileName,int startLine,
  
  //printf("Starting comment block at %s:%d\n",g_fileName.data(),startLine);
  doctokenizerYYlineno=startLine;
  QCString inpStr=input;
  uint inpLen = inpStr.length();
  if (inpLen>0 && inpStr.at(inpLen-1)!='\n')
  uint inpLen=qstrlen(input);
  QCString inpStr = processCopyDoc(input,inpLen);
  if (inpStr.isEmpty() || inpStr.at(inpStr.length()-1)!='\n')
  {
    inpStr+='\n';
  }
  //printf("processCopyDoc(in='%s' out='%s')\n",input,inpStr.data());
  doctokenizerYYinit(inpStr,g_fileName);

  // build abstract syntax tree