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;