diff --git a/utils/HelpGen/Makefile b/utils/HelpGen/Makefile index 44562e2d9c..f55affa8e7 100644 --- a/utils/HelpGen/Makefile +++ b/utils/HelpGen/Makefile @@ -30,6 +30,7 @@ SRCS = src/cjparser.cpp\ src/wx/hash.cpp\ src/wx/log.cpp\ src/wx/file.cpp\ + src/wx/dynarray.cpp\ src/wx/list.cpp OBJS = obj/cjparser.o\ @@ -45,6 +46,7 @@ OBJS = obj/cjparser.o\ obj/wx/hash.o\ obj/wx/log.o\ obj/wx/file.o\ + obj/wx/dynarray.o\ obj/wx/list.o # tested with egcs 1.1.1 (add more compilers here) @@ -52,7 +54,7 @@ CC = g++ DEFINES = -DwxUSE_NOGUI=1 -D__UNIX__ -D__WXGTK__ -DHAVE_VSNPRINTF # this might not work with some versions of gcc - try just -g then -DEBUG = -ggdb +DEBUG = -ggdb -D__WXDEBUG__ INCLUDE = -I./include CPPFLAGS = $(INCLUDE) $(DEFINES) $(DEBUG) -c -Wall @@ -64,7 +66,7 @@ CPPFLAGS = $(INCLUDE) $(DEFINES) $(DEBUG) -c -Wall obj/%.o: src/%.cpp $(CC) $(CPPFLAGS) -o $@ $< -all: links $(PROG) +all: $(PROG) $(PROG) : $(OBJS) $(CC) -o $(PROG) $(DEBUG) -Wall $(OBJS) diff --git a/utils/HelpGen/include/srcparser.h b/utils/HelpGen/include/srcparser.h index 39ad1c3c25..6d0a550d36 100644 --- a/utils/HelpGen/include/srcparser.h +++ b/utils/HelpGen/include/srcparser.h @@ -367,8 +367,8 @@ public: // TRUE, if there is at least one entry // in the comment list of this context bool HasComments(); - inline MCommentListT& GetCommentList() - { return mComments; } + MCommentListT& GetCommentList() { return mComments; } + const MCommentListT& GetCommentList() const { return mComments; } // should be overriden, if the context supports sorting // of it's members diff --git a/utils/HelpGen/src/HelpGen.cpp b/utils/HelpGen/src/HelpGen.cpp index af3023ecdf..d4bb767050 100644 --- a/utils/HelpGen/src/HelpGen.cpp +++ b/utils/HelpGen/src/HelpGen.cpp @@ -18,9 +18,11 @@ 2. Document typedefs 3. Document global variables 4. Document #defines + +5. Program options (ii) plans for version 2 1. Use wxTextFile for direct file access to avoid one scan method problems + 2. Use command line parsrer class for the options */ @@ -39,6 +41,7 @@ #include #include #include + #include #endif // WX_PRECOMP // C++ parsing classes @@ -52,12 +55,22 @@ // private functions // ----------------------------------------------------------------------------- -// return the label for the given function name -static wxString MakeLabel(const char *classname, const char *funcname); - +// return the label for the given function name (i.e. argument of \label) +static wxString MakeLabel(const char *classname, const char *funcname = NULL); + +// return the whole \helpref{arg}{arg_label} string +static wxString MakeHelpref(const char *argument); + // quotes special TeX characters in place static void TeXFilter(wxString* str); +// get all comments associated with this context +static wxString GetAllComments(const spContext& ctx); + +// get the string with current time (returns pointer to static buffer) +// timeFormat is used for the call of strftime(3) +static const char *GetCurrentTime(const char *timeFormat); + // ----------------------------------------------------------------------------- // private classes // ----------------------------------------------------------------------------- @@ -87,6 +100,7 @@ public: virtual void VisitClass( spClass& cl ); virtual void VisitEnumeration( spEnumeration& en ); virtual void VisitTypeDef( spTypeDef& td ); + virtual void VisitPreprocessorLine( spPreprocessorLine& pd ); virtual void VisitAttribute( spAttribute& attr ); virtual void VisitOperation( spOperation& op ); virtual void VisitParameter( spParameter& param ); @@ -125,6 +139,9 @@ protected: wxString m_textStoredEnums, m_textStoredTypedefs, m_textStoredFunctionComment; + + // headers included by this file + wxArrayString m_headers; }; // ----------------------------------------------------------------------------- @@ -135,23 +152,44 @@ protected: // implementation // ============================================================================= +// this function never returns +static void usage() +{ + wxLogError("usage: HelpGen [-q|-v]
\n"); + + exit(1); +} + int main(int argc, char **argv) { if ( argc < 2 ) { - wxLogError("usage: %s
\n", argv[0]); - - return 1; + usage(); } - // be verbose - wxLog::GetActiveTarget()->SetVerbose(); + int first; + for ( first = 1; (first < argc) && argv[first][0] == '-'; first++ ) { + switch ( argv[first][1] ) { + case 'v': + // be verbose + wxLog::GetActiveTarget()->SetVerbose(); + break; + + case 'q': + // be quiet + wxLog::GetActiveTarget()->SetVerbose(false); + break; + + default: + usage(); + } + } // create a parser object and a visitor derivation CJSourceParser parser; HelpGenVisitor visitor; // parse all files - for ( int i = 1; i < argc; i++ ) { + for ( int i = first; i < argc; i++ ) { spContext *ctxTop = parser.ParseFile(argv[i]); if ( !ctxTop ) { wxLogWarning("File '%s' couldn't be processed.", argv[i]); @@ -181,6 +219,11 @@ void HelpGenVisitor::Reset() m_inFunction = m_inTypesSection = m_inMethodSection = false; + + m_textStoredTypedefs = + m_textStoredEnums = + m_textStoredFunctionComment = ""; + m_headers.Empty(); } void HelpGenVisitor::InsertTypedefDocs() @@ -236,11 +279,15 @@ void HelpGenVisitor::CloseFunction() void HelpGenVisitor::EndVisit() { CloseFunction(); + + wxLogInfo("%s: finished parsing the current file.", + GetCurrentTime("%H:%M:%S")); } void HelpGenVisitor::VisitFile( spFile& file ) { - wxLogInfo("Parsing classes from file '%s'...", file.mFileName.c_str()); + wxLogInfo("%s: started to parse classes from file '%s'...", + GetCurrentTime("%H:%M:%S"), file.mFileName.c_str()); } void HelpGenVisitor::VisitClass( spClass& cl ) @@ -276,27 +323,82 @@ void HelpGenVisitor::VisitClass( spClass& cl ) // write out the header { - time_t timeNow = time(NULL); wxString header; - header.Printf("% automatically generated by HelpGen from %s at " - "%s" // no '\n' here because ctime() inserts one + header.Printf("% automatically generated by HelpGen from %s at %s\n" "\\section{\\class{%s}}\\label{%s}\n", - filename.c_str(), ctime(&timeNow), + filename.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"), name.c_str(), wxString(name).MakeLower().c_str()); totalText << header << '\n'; } + // if the header includes other headers they must be related to it... try to + // automatically generate the "See also" clause + if ( !m_headers.IsEmpty() ) { + // correspondence between wxWindows headers and class names + static const char *headers[] = { + "object", + "defs", + "string", + "dynarray", + "file", + "time", + }; + + // NULL here means not to insert anything in "See also" for the + // corresponding header + static const char *classes[] = { + NULL, + NULL, + NULL, + NULL, + "wxFile", + "wxTime", + }; + + wxASSERT_MSG( WXSIZEOF(headers) == WXSIZEOF(classes), + "arrays must be in sync!" ); + + wxArrayInt interestingClasses; + + size_t count = m_headers.Count(), index; + for ( size_t n = 0; n < count; n++ ) { + wxString baseHeaderName = m_headers[n].Before('.'); + if ( baseHeaderName(0, 3) != "wx/" ) + continue; + + baseHeaderName.erase(0, 3); + for ( index = 0; index < WXSIZEOF(headers); index++ ) { + if ( Stricmp(baseHeaderName, headers[index]) == 0 ) + break; + } + + if ( (index < WXSIZEOF(headers)) && classes[index] ) { + // interesting header + interestingClasses.Add(index); + } + } + + if ( !interestingClasses.IsEmpty() ) { + // do generate "See also" clause + totalText << "\\wxheading{See also:}\n\n"; + + count = interestingClasses.Count(); + for ( index = 0; index < count; index++ ) { + if ( index > 0 ) + totalText << ", "; + + totalText << MakeHelpref(classes[interestingClasses[index]]); + } + + totalText << "\n\n"; + } + } + // the comment before the class generally explains what is it for so put it // in place of the class description if ( cl.HasComments() ) { - wxString comment; - const MCommentListT& comments = cl.GetCommentList(); - for ( MCommentListT::const_iterator i = comments.begin(); - i != comments.end(); - i++ ) { - comment << (*i)->GetText(); - } + wxString comment = GetAllComments(cl); totalText << '\n' << comment << '\n'; } @@ -351,7 +453,7 @@ void HelpGenVisitor::VisitEnumeration( spEnumeration& en ) } // simply copy the enum text in the docs - wxString enumeration; + wxString enumeration = GetAllComments(en); enumeration << "{\\small \\begin{verbatim}\n" << en.mEnumContent << "\n\\end{verbatim}}\n"; @@ -377,7 +479,47 @@ void HelpGenVisitor::VisitTypeDef( spTypeDef& td ) { CloseFunction(); - wxFAIL_MSG("don't know how to document typedefs yet"); + if ( m_inMethodSection ) { + // FIXME that's a bug, but tell the user aboit it nevertheless... + wxLogWarning("typedef '%s' ignored, please put it before the class " + "methods.", td.GetName().c_str()); + return; + } + + wxString typedefdoc; + typedefdoc << "{\\small \\begin{verbatim}\n" + << "typedef " << td.mOriginalType << ' ' << td.GetName() + << "\n\\end{verbatim}}\n" + << GetAllComments(td); + + // remember for later use if we're not inside a class yet + if ( !m_inClass ) { + if ( !m_textStoredTypedefs.IsEmpty() ) { + m_textStoredTypedefs << '\n'; + } + + m_textStoredTypedefs << typedefdoc; + } + else { + // write the header for this section if not done yet + InsertDataStructuresHeader(); + + typedefdoc << '\n'; + m_file.WriteTeX(typedefdoc); + } +} + +void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine& pd ) +{ + switch ( pd.GetStatementType() ) { + case SP_PREP_DEF_INCLUDE_FILE: + m_headers.Add(pd.CPP_GetIncludedFileNeme()); + break; + + case SP_PREP_DEF_DEFINE_SYMBOL: + // TODO decide if it's a constant and document it if it is + break; + } } void HelpGenVisitor::VisitAttribute( spAttribute& attr ) @@ -388,7 +530,7 @@ void HelpGenVisitor::VisitAttribute( spAttribute& attr ) if ( !m_inClass || !attr.IsPublic() ) return; - wxFAIL_MSG("don't know how to document member vars yet"); + wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str()); } void HelpGenVisitor::VisitOperation( spOperation& op ) @@ -413,13 +555,7 @@ void HelpGenVisitor::VisitOperation( spOperation& op ) m_inFunction = m_isFirstParam = true; - m_textStoredFunctionComment.Empty(); - const MCommentListT& comments = op.GetCommentList(); - for ( MCommentListT::const_iterator i = comments.begin(); - i != comments.end(); - i++ ) { - m_textStoredFunctionComment << (*i)->GetText(); - } + m_textStoredFunctionComment = GetAllComments(op); // start function documentation wxString totalText; @@ -434,10 +570,11 @@ void HelpGenVisitor::VisitOperation( spOperation& op ) } totalText.Printf("\\membersection{%s::%s}\\label{%s}\n" - "\\%sfunc{%s}{%s}{", + "\\%sfunc{%s%s}{%s}{", classname, funcname, MakeLabel(classname, funcname).c_str(), op.mIsConstant ? "const" : "", + op.mIsVirtual ? "virtual " : "", op.mRetType.c_str(), funcname); @@ -475,7 +612,7 @@ void HelpGenVisitor::VisitParameter( spParameter& param ) static wxString MakeLabel(const char *classname, const char *funcname) { wxString label(classname); - if ( funcname[0] == '\\' ) { + if ( funcname && funcname[0] == '\\' ) { // we may have some special TeX macro - so far only \destruct exists, // but may be later others will be added static const char *macros[] = { "destruct" }; @@ -498,13 +635,22 @@ static wxString MakeLabel(const char *classname, const char *funcname) } } - label << funcname; + if ( funcname ) + label << funcname; label.MakeLower(); return label; } +static wxString MakeHelpref(const char *argument) +{ + wxString helpref; + helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}'; + + return helpref; +} + static void TeXFilter(wxString* str) { // FIXME may be done much more quickly @@ -512,4 +658,31 @@ static void TeXFilter(wxString* str) str->Replace("_", "\\_"); } +static wxString GetAllComments(const spContext& ctx) +{ + wxString comment; + const MCommentListT& comments = ctx.GetCommentList(); + for ( MCommentListT::const_iterator i = comments.begin(); + i != comments.end(); + i++ ) { + comment << (*i)->GetText(); + } + + return comment; +} + +static const char *GetCurrentTime(const char *timeFormat) +{ + static char s_timeBuffer[128]; + time_t timeNow; + struct tm *ptmNow; + + time(&timeNow); + ptmNow = localtime(&timeNow); + + strftime(s_timeBuffer, WXSIZEOF(s_timeBuffer), timeFormat, ptmNow); + + return s_timeBuffer; +} + /* vi: set tw=80 et ts=4 sw=4: */