New Features

Brendon Costa <[email protected]>
Newsgroups gmane.text.doxygen.general,gmane.text.doxygen.devel
Message-ID <[email protected]>
Hi all,

I have got some patches against doxygen 1.5.1 that provide additional
functionality that I require for my project:
http://edoc.sourceforge.net/ which generates files with auto generated
documentation of C++ exception information that can be processed using
doxygen. I also think these patches can be beneficial for others and I
would like to submit them for inclusion in future doxygen versions.

Before submitting I will need to update the patches to be against the
doxygen CVS head, however I just wanted to ask if they would be accepted
before I go to the work of updating them.


The patches are:
-------------------------------
1) Provide an additional configuration option to allow appending of
multiple detail sections instead of overwriting.

A good example of this is:

//==============================================================================
/** \brief FirstBriefMultipleDetail brief 1.
 *
 * FirstBriefMultipleDetail detailed 1.
 */
void FirstBriefMultipleDetail();

/** \fn FirstBriefMultipleDetail()
 *
 * FirstBriefMultipleDetail detailed 2.
 */

/** \fn FirstBriefMultipleDetail()
 *
 * FirstBriefMultipleDetail detailed 3.
 */


Will generate docs like:
Brief: FirstBriefMultipleDetail brief 1.
Detail:
	FirstBriefMultipleDetail detailed 1.
	FirstBriefMultipleDetail detailed 2.
	FirstBriefMultipleDetail detailed 3.



-------------------------------
2) Provide ability to match instantiated template types.

E.g. Will successfully document the following:

template <typename T, typename U = float>
class Test {};
void ExatcMatchWithDefaults(Test<int> one);

/** \fn ExatcMatchWithDefaults(Test<int, float>)
 *
 * \brief ExatcMatchWithDefaults Brief.
 *
 * ExatcMatchWithDefaults Detailed.
 */

Note: This is a very simple example, the template type matching is
recursive and will work with more complex examples.

There is however a limitation in that it performs a best effort match
with the information that it has. I.e. It will consider the following to
be the same (Continuing from above example):
ExatcMatchWithDefaults(Test<int>)
ExatcMatchWithDefaults(Test<int, int>)

OR the following to also be the same:
ExatcMatchWithDefaults(Test<int>)
ExatcMatchWithDefaults(Test<int, float>)


This is because the parser has not been modified to obtain the default
template arg values from the source files. It will however correctly
differentiate:
ExatcMatchWithDefaults(Test<int, int>)
ExatcMatchWithDefaults(Test<int, float>)

as being different.



-------------------------------
3) Order of parsing/processing input files is done in the order that the
user specifies in the config file not in alphabetical order.




I have attached a patch file to this email in case people wish to have a
quick look at the patches against 1.5.1. These were created from CVS:
Release_1_5_1 using

cvs diff -u >../edoc.diff

and can be applied to a fresh Release_1_5_1 checkout using something like:
cd <doxdir>
patch -p0 < ../edoc.diff


Thanks,
Brendon.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

_______________________________________________
Doxygen-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/doxygen-users
edoc.diff (text/x-patch, 18.4 KB)
? VERSION
? packages/rpm/doxygen.spec
Index: Doxyfile
===================================================================
RCS file: /cvsroot/doxygen/Doxyfile,v
retrieving revision 1.29
diff -u -r1.29 Doxyfile
--- Doxyfile	27 Dec 2005 20:36:37 -0000	1.29
+++ Doxyfile	25 Aug 2007 02:48:30 -0000
@@ -13,6 +13,7 @@
 REPEAT_BRIEF           = YES
 ABBREVIATE_BRIEF       = 
 ALWAYS_DETAILED_SEC    = NO
+APPEND_MULT_DETAIL     = NO
 INLINE_INHERITED_MEMB  = NO
 FULL_PATH_NAMES        = YES
 STRIP_FROM_PATH        = 
Index: addon/doxmlparser/Doxyfile
===================================================================
RCS file: /cvsroot/doxygen/addon/doxmlparser/Doxyfile,v
retrieving revision 1.3
diff -u -r1.3 Doxyfile
--- addon/doxmlparser/Doxyfile	18 Feb 2002 21:34:00 -0000	1.3
+++ addon/doxmlparser/Doxyfile	25 Aug 2007 02:48:31 -0000
@@ -16,6 +16,7 @@
 BRIEF_MEMBER_DESC      = YES
 REPEAT_BRIEF           = YES
 ALWAYS_DETAILED_SEC    = NO
+APPEND_MULT_DETAIL     = NO
 INLINE_INHERITED_MEMB  = NO
 FULL_PATH_NAMES        = NO
 STRIP_FROM_PATH        = 
Index: addon/doxmlparser/Doxyfile.impl
===================================================================
RCS file: /cvsroot/doxygen/addon/doxmlparser/Doxyfile.impl,v
retrieving revision 1.2
diff -u -r1.2 Doxyfile.impl
--- addon/doxmlparser/Doxyfile.impl	7 Apr 2002 17:23:30 -0000	1.2
+++ addon/doxmlparser/Doxyfile.impl	25 Aug 2007 02:48:31 -0000
@@ -16,6 +16,7 @@
 BRIEF_MEMBER_DESC      = YES
 REPEAT_BRIEF           = YES
 ALWAYS_DETAILED_SEC    = NO
+APPEND_MULT_DETAIL     = NO
 INLINE_INHERITED_MEMB  = NO
 FULL_PATH_NAMES        = NO
 STRIP_FROM_PATH        = 
Index: doc/config.doc
===================================================================
RCS file: /cvsroot/doxygen/doc/config.doc,v
retrieving revision 1.119
diff -u -r1.119 config.doc
--- doc/config.doc	16 Jul 2006 20:10:05 -0000	1.119
+++ doc/config.doc	25 Aug 2007 02:48:32 -0000
@@ -60,6 +60,7 @@
 \refitem cfg_allexternals ALLEXTERNALS 
 \refitem cfg_alphabetical_index ALPHABETICAL_INDEX 
 \refitem cfg_always_detailed_sec ALWAYS_DETAILED_SEC
+\refitem cfg_append_mult_detail APPEND_MULT_DETAIL
 \refitem cfg_binary_toc BINARY_TOC
 \refitem cfg_builtin_stl_support BUILTIN_STL_SUPPORT
 \refitem cfg_brief_member_desc BRIEF_MEMBER_DESC 
@@ -321,6 +322,13 @@
  doxygen will generate a detailed section even if there is only a brief
  description.
 
+\anchor cfg_append_mult_detail
+<dt>\c APPEND_MULT_DETAIL <dd>
+ \addindex APPEND_MULT_DETAIL
+ If the \c APPEND_MULT_DETAIL is set to\c YES then
+ doxygen will append detail documentation from multiple members instead of the
+ usual behaviour of overwriting. 
+
 \anchor cfg_inline_inherited_memb
 <dt>\c INLINE_INHERITED_MEMB <dd>
 \addindex INLINE_INHERITED_MEMB
Index: qtools/Doxyfile
===================================================================
RCS file: /cvsroot/doxygen/qtools/Doxyfile,v
retrieving revision 1.6
diff -u -r1.6 Doxyfile
--- qtools/Doxyfile	10 Sep 2006 20:46:22 -0000	1.6
+++ qtools/Doxyfile	25 Aug 2007 02:48:35 -0000
@@ -93,6 +93,14 @@
 
 ALWAYS_DETAILED_SEC    = NO
 
+# If the APPEND_MULT_DETAIL tag is set to YES Doxygen will
+# append multiple detail documentation fields for a given
+# member rather than just using the last discovered detailed
+# documentation.
+# Set to YES to enable appending instead of overwriting.
+
+APPEND_MULT_DETAIL    = NO
+
 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
 # members of a class in the documentation of that class as if those members were 
 # ordinary class members. Constructors, destructors and assignment operators of 
Index: src/config.l
===================================================================
RCS file: /cvsroot/doxygen/src/config.l,v
retrieving revision 1.153
diff -u -r1.153 config.l
--- src/config.l	9 Aug 2006 08:29:53 -0000	1.153
+++ src/config.l	25 Aug 2007 02:48:48 -0000
@@ -1435,6 +1435,15 @@
                     FALSE
                  );
   cb = addBool(
+                    "APPEND_MULT_DETAIL",
+                    "If the APPEND_MULT_DETAIL tag is set to YES Doxygen will \n"
+                    "append multiple detail documentation fields for a given \n"
+                    "member rather than just using the last discovered detailed \n"
+                    "documentation. \n"
+                    "Set to YES to enable appending instead of overwriting. \n",
+                    FALSE
+                 );
+  cb = addBool(
                     "INLINE_INHERITED_MEMB",
                     "If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all \n"
 		    "inherited members of a class in the documentation of that class as if those \n"
Index: src/doxygen.cpp
===================================================================
RCS file: /cvsroot/doxygen/src/doxygen.cpp,v
retrieving revision 1.223
diff -u -r1.223 doxygen.cpp
--- src/doxygen.cpp	29 Oct 2006 18:39:27 -0000	1.223
+++ src/doxygen.cpp	25 Aug 2007 02:48:58 -0000
@@ -4541,6 +4541,7 @@
                    NamespaceSDict *
                   )
 {
+  static bool appendMultDetail = Config_getBool("APPEND_MULT_DETAIL");
   Entry *root = rootNav->entry();
   //printf("addMemberDocs: `%s'::`%s' `%s' funcDecl=`%s' memSpec=%d\n",
   //     root->parent->name.data(),md->name().data(),md->argsString(),funcDecl,root->memSpec);
@@ -4600,21 +4601,37 @@
   {
     //printf("Adding docs md->docs=`%s' root->docs=`%s'!\n",
     //     md->documentation().data(),root->doc.data());
-    // documentation outside a compound overrides the documentation inside it
-    if ( /* !md->isStatic() && !root->stat &&   do not replace doc of a static */
-        (
-         md->documentation().isEmpty() ||    /* no docs yet */
-         (rootNav->parent()->name().isEmpty() &&     /* or overwrite prototype docs */
-          !root->proto && md->isPrototype()  /* with member definition docs */
-         )
-        ) && !root->doc.isEmpty() 
-       )
+    // If append is set in the configuration then we will append the 
+    // documentation regardless. Otherwise follow standard rules for append vs
+    // overwrite of the docs.
+    if (appendMultDetail)
+    {
+      // @@@Brendon Appends detail documentation when multiple detail sections 
+      // match the current item.
+      //printf("append!\n");
+      QCString s = md->documentation();
+      s += "\n\n";
+      s += root->doc;
+      md->setDocumentation(s,md->docFile(),md->docLine());
+    }
+    else
     {
-      //printf("overwrite!\n");
-      md->setDocumentation(root->doc,root->docFile,root->docLine);
-      md->setDocsForDefinition(!root->proto);
+       // documentation outside a compound overrides the documentation inside it
+       if ( /* !md->isStatic() && !root->stat &&   do not replace doc of a static */
+           (
+            md->documentation().isEmpty() ||    /* no docs yet */
+            (rootNav->parent()->name().isEmpty() &&     /* or overwrite prototype docs */
+             !root->proto && md->isPrototype()  /* with member definition docs */
+            )
+           ) && !root->doc.isEmpty() 
+          )
+       {
+         //printf("overwrite!\n");
+         md->setDocumentation(root->doc,root->docFile,root->docLine);
+         md->setDocsForDefinition(!root->proto);
+       }
     }
-
+    
     //printf("Adding brief md->brief=`%s' root->brief=`%s'!\n",
     //     md->briefDescription().data(),root->brief.data());
     // brief descriptions inside a compound override the documentation 
@@ -6742,27 +6759,26 @@
 static void generateFileSources()
 {
   if (documentedHtmlFiles==0) return;
-  if (Doxygen::inputNameList->count()>0)
+  // @@@Brendon Changed this code to ensure that sources are parsed in the 
+  // order they are specified in the configuration file and not alphabetical
+  // order.
+  for (size_t i = 0; i < Doxygen::inputNameList->count(); i++)
   {
-    FileNameListIterator fnli(*Doxygen::inputNameList); 
-    FileName *fn;
-    for (;(fn=fnli.current());++fnli)
+    FileName *fn = Doxygen::inputNameList->at(i);
+    FileNameIterator fni(*fn);
+    FileDef *fd;
+    for (;(fd=fni.current());++fni)
     {
-      FileNameIterator fni(*fn);
-      FileDef *fd;
-      for (;(fd=fni.current());++fni)
+      if (fd->generateSourceFile()) // sources need to be shown in the output
       {
-        if (fd->generateSourceFile()) // sources need to be shown in the output
-        {
-          msg("Generating code for file %s...\n",fd->docName().data());
-          fd->writeSource(*outputList);
-        }
-        else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
-          // we needed to parse the sources even if we do not show them
-        {
-          msg("Parsing code for file %s...\n",fd->docName().data());
-          fd->parseSource();
-        }
+        msg("Generating code for file %s...\n",fd->docName().data());
+        fd->writeSource(*outputList);
+      }
+      else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
+        // we needed to parse the sources even if we do not show them
+      {
+        msg("Parsing code for file %s...\n",fd->docName().data());
+        fd->parseSource();
       }
     }
   }
@@ -8131,9 +8147,11 @@
 
 static void parseFiles(Entry *root,EntryNav *rootNav)
 {
-  QCString *s=inputFiles.first();
-  while (s)
+  // @@@Brendon Updated to parse files in order specified in the configuration 
+  // file, not just based on the iterator order.
+  for (size_t i = 0; i < inputFiles.count(); i++)
   {
+    QCString* s = inputFiles.at(i);
     QCString fileName=*s;
     QCString extension;
     int ei = fileName.findRev('.');
@@ -8174,8 +8192,6 @@
     FileDef *fd=findFileDef(Doxygen::inputNameDict,fileName,ambig);
     ASSERT(fd!=0);
     root->createNavigationIndex(rootNav,g_storage,fd);
-
-    s=inputFiles.next();
   }
 }
 
@@ -8333,7 +8349,8 @@
               {
                 fn = new FileName(fi.absFilePath(),name);
                 fn->append(fd);
-                if (fnList) fnList->inSort(fn);
+                // @@@Brendon More updates for config file order.
+                if (fnList) fnList->append(fn);
                 fnDict->insert(name,fn);
               }
             }
Index: src/util.cpp
===================================================================
RCS file: /cvsroot/doxygen/src/util.cpp,v
retrieving revision 1.191
diff -u -r1.191 util.cpp
--- src/util.cpp	29 Oct 2006 18:39:27 -0000	1.191
+++ src/util.cpp	25 Aug 2007 02:49:03 -0000
@@ -3145,6 +3145,200 @@
   return extractCanonicalType(d,fs,type);
 }
 
+#include <qvaluelist.h>
+
+// @@@Brendon
+static bool BreakUpType(QString type, QString& prefix, QValueList<QString>& params, QString& suffix)
+{
+   params.clear();
+   
+   // @@@Brendon How to handle operator < and operator >
+   size_t depth = 0;
+   size_t start_param = -1;
+   for (size_t i = 0; i < type.length(); i++)
+   {
+      if (type[i] == '<')
+      {
+         //printf("< index: %d, depth: %d\n", i, depth);
+         depth++;
+         if (depth == 1)
+         {
+            // Then we have a prefix.
+            prefix = type.left(i).stripWhiteSpace();
+            //printf("Prefix: %s, index: %d\n", prefix.c_str(), i);
+            start_param = i + 1;
+         }
+      }
+      else if (type[i] == '>')
+      {
+         //printf("> index: %d, depth: %d\n", i, depth);
+         depth--;
+         if (depth == 0)
+         {
+            if (start_param != (size_t)-1)
+            {
+               params.append(type.mid(start_param, i - start_param).stripWhiteSpace());
+               //printf("Added param: %s, index: %d, start_param: %d\n", params.back().c_str(), i, start_param);
+            }
+            // Then we have finished parsing template params, lets obtain the suffix and return.
+            //suffix = TrimWhitespace(type.substr(i + 1));
+            suffix = type.mid(i + 1).stripWhiteSpace();
+            //printf("Suffix: %s, index: %d\n", suffix.c_str(), i);
+            return true;
+         }
+      }
+      else if (type[i] == ',')
+      {
+         // If inside top level template then we have a new parameter.
+         if (depth == 1)
+         {
+            //params.push_back(TrimWhitespace(type.substr(start_param, i - start_param)));
+            params.append(type.mid(start_param, i - start_param).stripWhiteSpace());
+            //printf("Added param: %s, index: %d, start_param: %d\n", params.back().c_str(), i, start_param);
+            start_param = i + 1;
+         }
+      }
+   }
+   
+   prefix = type.stripWhiteSpace();
+   //printf("No template info found. Returning prefix: %s\n", prefix.c_str());
+   return false;
+}
+
+// @@@Brendon Added special function for comparing two types.
+// This function will return true if the two types are considered the same.
+// It differens from comparison of canonical names in that it breaks the 
+// canonical names into various parts for template types and will match types
+// where default template parameters have been specified explicitly with those
+// that have not been.
+static bool sameType(const QString& left, const QString& right)
+{
+   //std::string left = left_in.data();
+   //std::string right = right_in.data();
+   
+   QString left_prefix;
+   QString left_suffix;
+   QValueList<QString> left_params;
+
+   QString right_prefix;
+   QString right_suffix;
+   QValueList<QString> right_params;
+   
+   BreakUpType(left, left_prefix, left_params, left_suffix);
+   BreakUpType(right, right_prefix, right_params, right_suffix);
+
+   // @@@Brendon It seems doxygen has trouble determining the namespace of 
+   // items inside template parameters. In order to get some sembelance of 
+   // correct matching i will use the shortest of the two strings as a 
+   // reference, and if the longer ends with the same contents then there is 
+   // a GOOD chance that it is a match. This will fail if for example in two 
+   // different namespaces there is a class defined called Blah. This method 
+   // may be unable to determine the difference between the two types of Blah, 
+   // but only when used in an overloaded function prototype.
+   
+   // Note: We also need to take care of the " &" hack too.
+   
+   // So first we remove any " &" from the back of the prefix.
+   //printf("CHECK: Removing trailing & from left_prefix: %s\n", left_prefix.c_str());
+   if (left_prefix.length() >= 1 && left_prefix.mid(left_prefix.length() - 1) == "&")
+   {
+      //printf("Removing trailing & from left_prefix: %s\n", left_prefix.c_str());
+      left_prefix = left_prefix.mid(0, left_prefix.length() - 1).stripWhiteSpace();
+   }
+
+   //printf("CHECK: Removing trailing & from right_prefix: %s\n", right_prefix.c_str());
+   if (right_prefix.length() >= 1 && right_prefix.mid(right_prefix.length() - 1) == "&")
+   {
+      //printf("Removing trailing & from right_prefix: %s\n", right_prefix.c_str());
+      right_prefix = right_prefix.mid(0, right_prefix.length() - 1).stripWhiteSpace();
+   }
+   
+   //printf("Obtaining min size of prefixes.\n");
+   size_t min = left_prefix.length();
+   min = (right_prefix.length() < min ? right_prefix.length() : min);
+   
+   // Check for zero length prefixes. If one is 0 and the other is not then 
+   // assume a failed match in case we are comparing a type ending in & with
+   // a compound type.
+   if (!min && left_prefix.length() != right_prefix.length())
+   {
+      printf("One prefix was empty the other had data. left: %s, right: %s\n", left_prefix.data(), right_prefix.data());
+      return false;
+   }
+   
+   //printf("Finally getting proper values.\n");
+
+   //printf("Left prefix: %s, size: %d, min: %d\n", left_prefix.c_str(), left_prefix.size(), min);
+   left_prefix = left_prefix.mid(left_prefix.length() - min).stripWhiteSpace();
+
+   //printf("Right prefix: %s, size: %d, min: %d\n", right_prefix.c_str(), right_prefix.size(), min);
+   right_prefix = right_prefix.mid(right_prefix.length() - min).stripWhiteSpace();
+   //printf("%d: Comparing prefixes left: %s, right: %s\n", __FILE__, left_prefix.c_str(), right_prefix.c_str());
+   //fflush(stdout);
+   
+   
+#define DISPLAY_DETAILS \
+   std::ostringstream stream; \
+   std::string p = "\t"; \
+   stream << "Comparing- \n"; \
+   stream   << p << "left: " << left << "\n" \
+            << p << p << "prefix: " << left_prefix << "\n" \
+            << p << p << "suffix: " << left_suffix << "\n" \
+            << p << p << "params: " << "\n"; \
+   for (std::list<std::string>::iterator it = left_params.begin(); \
+      it != left_params.end(); it++) \
+   { \
+      stream << p << p << p << *it << "\n"; \
+   } \
+   stream   << p << "right: " << right << "\n" \
+            << p << p << "prefix: " << right_prefix << "\n" \
+            << p << p << "suffix: " << right_suffix << "\n" \
+            << p << p << "params: " << "\n"; \
+   for (std::list<std::string>::iterator it = right_params.begin(); \
+      it != right_params.end(); it++) \
+   { \
+      stream << p << p << p << *it << "\n"; \
+   } \
+   printf("%s\n", stream.str().c_str());
+
+#undef DISPLAY_DETAILS
+#define DISPLAY_DETAILS
+
+   
+   if (left_prefix != right_prefix)
+   {
+      DISPLAY_DETAILS
+      //printf("%d: The types prefixes differ left: %s, right: %s\n", __LINE__, left.c_str(), right.c_str());
+      return false;
+   }
+
+   
+   QValueList<QString>::Iterator lt = left_params.begin();
+   QValueList<QString>::Iterator rt = right_params.begin();
+   for (; lt != left_params.end() && rt != right_params.end(); lt++, rt++)
+   {
+      if (!sameType((*lt), QString(*rt)))
+      {
+         DISPLAY_DETAILS
+         //printf("%d: The types template params differ left: %s, right: %s\n", __LINE__, left.c_str(), right.c_str());
+         return false;
+      }
+   }
+   
+   if (right_suffix != left_suffix)
+   {
+      if (!sameType(left_suffix, right_suffix))
+      {
+         DISPLAY_DETAILS
+         //printf("%d: The types suffixes differ left: %s, right: %s\n", __LINE__, left.c_str(), right.c_str());
+         return false;
+      }
+   }
+
+   //printf("%d: The types are the same left: %s, right: %s\n", __LINE__, left.c_str(), right.c_str());
+   return true;
+}
+
 static bool matchArgument2(
     Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
     Definition *dstScope,FileDef *dstFileScope,Argument *dstA
@@ -3191,7 +3385,10 @@
     dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
   }
 
-  if (srcA->canType==dstA->canType)
+  // @@@Brendon Changed from comparison of canonical type names to a more 
+  // complex comparison of types to allow template types with specified default
+  // params to match "more correctly"
+  if (sameType(srcA->canType, dstA->canType))
   {
     MATCH
       return TRUE;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.