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
*/
#ifndef WX_PRECOMP
#include <wx/string.h>
#include <wx/log.h>
- #include <wx/file.h>
+ #include <wx/dynarray.h>
#endif // WX_PRECOMP
+#include <wx/file.h>
+
// C++ parsing classes
#include "cjparser.h"
// 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)
+#ifdef GetCurrentTime
+#undef GetCurrentTime
+#endif
+
+static const char *GetCurrentTime(const char *timeFormat);
+
// -----------------------------------------------------------------------------
// private classes
// -----------------------------------------------------------------------------
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 );
wxString m_textStoredEnums,
m_textStoredTypedefs,
m_textStoredFunctionComment;
+
+ // headers included by this file
+ wxArrayString m_headers;
};
// -----------------------------------------------------------------------------
// implementation
// =============================================================================
+// this function never returns
+static void usage()
+{
+ wxLogError("usage: HelpGen [-q|-v] <header files...>\n");
+
+ exit(1);
+}
+
int main(int argc, char **argv)
{
if ( argc < 2 ) {
- wxLogError("usage: %s <header files...>\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]);
m_inFunction =
m_inTypesSection =
m_inMethodSection = false;
+
+ m_textStoredTypedefs =
+ m_textStoredEnums =
+ m_textStoredFunctionComment = "";
+ m_headers.Empty();
}
void HelpGenVisitor::InsertTypedefDocs()
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 )
// 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';
}
}
wxString baseclass = *i;
- derived << "\\helpref{" << baseclass << "}"
- "{ " << baseclass.MakeLower() << "}";
+ derived << "\\helpref{" << baseclass << "}";
+ derived << "{" << baseclass.MakeLower() << "}";
}
}
totalText << derived << "\n\n";
}
// 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";
{
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 )
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 )
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;
funcname = dtor;
}
- totalText.Printf("\\membersection{%s::%s}\\label{%s}\n"
- "\\%sfunc{%s}{%s}{",
+ totalText.Printf("\\membersection{%s::%s}\\label{%s}\n\n"
+ "\\%sfunc{%s%s}{%s}{",
classname, funcname,
MakeLabel(classname, funcname).c_str(),
op.mIsConstant ? "const" : "",
+ op.mIsVirtual ? "virtual " : "",
op.mRetType.c_str(),
funcname);
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" };
}
}
- 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
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: */