engineDoc.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "console/console.h"
  24. #include "console/engineAPI.h"
  25. #include "core/stream/fileStream.h"
  26. #include "console/consoleInternal.h"
  27. #include "console/compiler.h"
  28. #define USE_UNDOCUMENTED_GROUP
  29. /// @file
  30. /// Documentation generator for the current TorqueScript-based engine API.
  31. ///
  32. /// Be aware that this generator is solely for the legacy console system and
  33. /// is and will not be useful to the new engine API system. It will go away
  34. /// when the console interop is removed.
  35. /// Used to track unique groups encountered during
  36. /// the dump process.
  37. static HashTable<String,U32> smDocGroups;
  38. static void dumpDoc( Stream& stream, const char* text, bool checkUngrouped = true )
  39. {
  40. // Extract brief.
  41. String brief;
  42. if( text )
  43. {
  44. const char* briefTag = dStrstr( text, "@brief" );
  45. if( !briefTag )
  46. {
  47. const char* newline = dStrchr( text, '\n' );
  48. if( newline )
  49. {
  50. brief = String( text, newline - text );
  51. text = newline + 1;
  52. }
  53. else
  54. {
  55. brief = text;
  56. text = NULL;
  57. }
  58. }
  59. }
  60. // Write doc comment.
  61. if( !brief.isEmpty() )
  62. {
  63. stream.writeText( "@brief " );
  64. stream.writeText( brief );
  65. stream.writeText( "\r\n\r\n" );
  66. }
  67. if( text )
  68. stream.writeText( text );
  69. #ifdef USE_UNDOCUMENTED_GROUP
  70. if( checkUngrouped && ( !text || !dStrstr( text, "@ingroup" ) ) )
  71. {
  72. smDocGroups.insertUnique( "UNDOCUMENTED", 0 );
  73. stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" );
  74. }
  75. #endif
  76. }
  77. static void dumpFragment( Stream& stream, ConsoleDocFragment* fragment )
  78. {
  79. if( !fragment->mText || !fragment->mText[ 0 ] )
  80. return;
  81. // Emit doc text in comment.
  82. stream.writeText( "/*!\r\n" );
  83. stream.writeText( fragment->mText );
  84. stream.writeText( "*/\r\n\r\n" );
  85. // Emit definition, if any.
  86. if( fragment->mDefinition )
  87. {
  88. stream.writeText( fragment->mDefinition );
  89. stream.writeText( "\r\n" );
  90. }
  91. }
  92. static void dumpVariable( Stream& stream,
  93. Dictionary::Entry* entry,
  94. const char* inClass = NULL )
  95. {
  96. // Skip variables defined in script.
  97. if( entry->type <= Dictionary::Entry::TypeInternalString )
  98. return;
  99. // Skip internals... don't export them.
  100. if ( entry->mUsage &&
  101. ( dStrstr( entry->mUsage, "@hide" ) || dStrstr( entry->mUsage, "@internal" ) ) )
  102. return;
  103. // Split up qualified name.
  104. Vector< String > nameComponents;
  105. String( entry->name ).split( "::", nameComponents );
  106. if( !nameComponents.size() ) // Safety check.
  107. return;
  108. // Match filter.
  109. if( inClass )
  110. {
  111. // Make sure first qualifier in name components is a
  112. // namespace qualifier matching the given class name.
  113. if( nameComponents.size() <= 1 || dStricmp( nameComponents.first().c_str() + 1, inClass ) != 0 ) // Skip '$'.
  114. return;
  115. }
  116. else
  117. {
  118. // Make sure, this is *not* in a class namespace.
  119. if( nameComponents.size() > 1 && Con::lookupNamespace( nameComponents.first().c_str() + 1 )->mClassRep )
  120. return;
  121. }
  122. // Skip variables for which we can't decipher their type.
  123. ConsoleBaseType* type = ConsoleBaseType::getType( entry->type );
  124. if( !type )
  125. {
  126. Con::errorf( "Can't find type for variable '%s'", entry->name );
  127. return;
  128. }
  129. // Write doc comment.
  130. stream.writeText( "/*!\r\n" );
  131. if( !inClass )
  132. {
  133. stream.writeText( "@var " );
  134. stream.writeText( type->getTypeClassName() );
  135. stream.writeText( " " );
  136. stream.writeText( entry->name );
  137. stream.writeText( ";\r\n" );
  138. }
  139. dumpDoc( stream, entry->mUsage );
  140. stream.writeText( "*/\r\n" );
  141. // Write definition.
  142. const U32 numNameComponents = nameComponents.size();
  143. if( !inClass && numNameComponents > 1 )
  144. for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i )
  145. {
  146. stream.writeText( "namespace " );
  147. stream.writeText( nameComponents[ i ] );
  148. stream.writeText( " { " );
  149. }
  150. if( inClass )
  151. stream.writeText( "static " );
  152. if( entry->mIsConstant )
  153. stream.writeText( "const " );
  154. stream.writeText( type->getTypeClassName() );
  155. stream.writeText( " " );
  156. stream.writeText( nameComponents.last() );
  157. stream.writeText( ";" );
  158. if( !inClass && numNameComponents > 1 )
  159. for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i )
  160. stream.writeText( " } " );
  161. stream.writeText( "\r\n" );
  162. }
  163. static void dumpVariables( Stream& stream, const char* inClass = NULL )
  164. {
  165. const U32 hashTableSize = gEvalState.globalVars.hashTable->size;
  166. for( U32 i = 0; i < hashTableSize; ++ i )
  167. for( Dictionary::Entry* entry = gEvalState.globalVars.hashTable->data[ i ]; entry != NULL; entry = entry->nextEntry )
  168. dumpVariable( stream, entry, inClass );
  169. }
  170. static void dumpFunction( Stream &stream,
  171. bool isClassMethod,
  172. Namespace::Entry* entry )
  173. {
  174. String doc = entry->getDocString().trim();
  175. String prototype = entry->getPrototypeString().trim();
  176. // If the doc string contains @hide, skip this function.
  177. if( dStrstr( doc.c_str(), "@hide" ) || dStrstr( doc.c_str(), "@internal" ) )
  178. return;
  179. // Make sure we have a valid function prototype.
  180. if( prototype.isEmpty() )
  181. {
  182. Con::errorf( "Function '%s::%s' has no prototype!", entry->mNamespace->mName, entry->mFunctionName );
  183. return;
  184. }
  185. // See if it's a static method.
  186. bool isStaticMethod = false;
  187. if( entry->mHeader )
  188. isStaticMethod = entry->mHeader->mIsStatic;
  189. // Emit the doc comment.
  190. if( !doc.isEmpty() )
  191. {
  192. stream.writeText( "/*!\r\n" );
  193. // If there's no @brief, take the first line of the doc text body
  194. // as the description.
  195. const char* brief = dStrstr( doc, "@brief" );
  196. if( !brief )
  197. {
  198. String briefStr = entry->getBriefDescription( &doc );
  199. briefStr.trim();
  200. if( !briefStr.isEmpty() )
  201. {
  202. stream.writeText( "@brief " );
  203. stream.writeText(briefStr);
  204. stream.writeText( "\r\n\r\n" );
  205. }
  206. }
  207. stream.writeText( doc );
  208. // Emit @ingroup if it's not a class method.
  209. if ( !isClassMethod && !isStaticMethod ) // Extra static method check for static classes (which will come out as non-class namespaces).
  210. {
  211. const char *group = dStrstr( doc, "@ingroup" );
  212. if( group )
  213. {
  214. char groupName[ 256 ] = { 0 };
  215. dSscanf( group, "@ingroup %s", groupName );
  216. smDocGroups.insertUnique( groupName, 0 );
  217. }
  218. #ifdef USE_UNDOCUMENTED_GROUP
  219. else
  220. {
  221. smDocGroups.insertUnique( "UNDOCUMENTED", 0 );
  222. stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" );
  223. }
  224. #endif
  225. }
  226. stream.writeText( "*/\r\n" );
  227. }
  228. #ifdef USE_UNDOCUMENTED_GROUP
  229. else if( !isClassMethod )
  230. {
  231. smDocGroups.insertUnique( "UNDOCUMENTED", 0 );
  232. stream.writeText( "/*! UNDOCUMENTED!\r\n@ingroup UNDOCUMENTED\r\n */\r\n" );
  233. }
  234. #endif
  235. if( isStaticMethod )
  236. stream.writeText( "static " );
  237. stream.writeText( prototype );
  238. stream.writeText( ";\r\n" );
  239. }
  240. static void dumpNamespaceEntries( Stream &stream, Namespace *g, bool callbacks = false )
  241. {
  242. /// Only print virtual on methods that are members of
  243. /// classes as this allows doxygen to properly do overloads.
  244. const bool isClassMethod = g->mClassRep != NULL;
  245. // Go through all the entries in the namespace.
  246. for ( Namespace::Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext )
  247. {
  248. S32 eType = ewalk->mType;
  249. // We do not dump script defined functions... only engine exports.
  250. if( eType == Namespace::Entry::ConsoleFunctionType
  251. || eType == Namespace::Entry::GroupMarker )
  252. continue;
  253. if( eType == Namespace::Entry::ScriptCallbackType )
  254. {
  255. if( !callbacks )
  256. continue;
  257. }
  258. else if( callbacks )
  259. continue;
  260. dumpFunction( stream, isClassMethod, ewalk );
  261. }
  262. }
  263. static void dumpClassHeader( Stream &stream,
  264. const char *usage,
  265. const char *className,
  266. const char *superClassName )
  267. {
  268. if ( usage )
  269. {
  270. stream.writeText( "/*!\r\n" );
  271. stream.writeText( usage );
  272. const char *group = dStrstr( usage, "@ingroup" );
  273. if ( group )
  274. {
  275. char groupName[256] = { 0 };
  276. dSscanf( group, "@ingroup %s", groupName );
  277. smDocGroups.insertUnique( groupName, 0 );
  278. }
  279. #ifdef USE_UNDOCUMENTED_GROUP
  280. else
  281. {
  282. smDocGroups.insertUnique( "UNDOCUMENTED", 0 );
  283. stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" );
  284. }
  285. #endif
  286. stream.writeText( "\r\n*/\r\n" );
  287. }
  288. else
  289. {
  290. // No documentation string. Check whether ther is a separate
  291. // class doc fragement.
  292. bool haveClassDocFragment = false;
  293. if( className )
  294. {
  295. char buffer[ 1024 ];
  296. dSprintf( buffer, sizeof( buffer ), "@class %s", className );
  297. for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst;
  298. fragment != NULL; fragment = fragment->mNext )
  299. if( !fragment->mClass && dStrstr( fragment->mText, buffer ) != NULL )
  300. {
  301. haveClassDocFragment = true;
  302. break;
  303. }
  304. }
  305. #ifdef USE_UNDOCUMENTED_GROUP
  306. if( !haveClassDocFragment )
  307. {
  308. smDocGroups.insertUnique( "UNDOCUMENTED", 0 );
  309. stream.writeText( "/*! UNDOCUMENTED!\r\n@ingroup UNDOCUMENTED\r\n */\r\n" );
  310. }
  311. #endif
  312. }
  313. // Print out appropriate class header
  314. if ( superClassName )
  315. stream.writeText( String::ToString( "class %s : public %s {\r\npublic:\r\n", className, superClassName ? superClassName : "" ) );
  316. else if ( className )
  317. stream.writeText( String::ToString( "class %s {\r\npublic:\r\n", className ) );
  318. else
  319. stream.writeText( "namespace {\r\n" );
  320. }
  321. static void dumpClassMember( Stream &stream,
  322. const AbstractClassRep::Field& field )
  323. {
  324. stream.writeText( "/*!\r\n" );
  325. if( field.pFieldDocs && field.pFieldDocs[ 0 ] )
  326. {
  327. stream.writeText( "@brief " );
  328. String docs( field.pFieldDocs );
  329. S32 newline = docs.find( '\n' );
  330. if( newline == -1 )
  331. stream.writeText( field.pFieldDocs );
  332. else
  333. {
  334. String brief = docs.substr( 0, newline );
  335. String body = docs.substr( newline + 1 );
  336. stream.writeText( brief );
  337. stream.writeText( "\r\n\r\n" );
  338. stream.writeText( body );
  339. }
  340. stream.writeText( "\r\n" );
  341. }
  342. const bool isDeprecated = ( field.type == AbstractClassRep::DeprecatedFieldType );
  343. if( isDeprecated )
  344. stream.writeText( "@deprecated This member is deprecated and its value is always undefined.\r\n" );
  345. stream.writeText( "*/\r\n" );
  346. ConsoleBaseType* cbt = ConsoleBaseType::getType( field.type );
  347. const char* type = ( cbt ? cbt->getTypeClassName() : "" );
  348. if( field.elementCount > 1 )
  349. stream.writeText( String::ToString( "%s %s[ %i ];\r\n", isDeprecated ? "deprecated" : type, field.pFieldname, field.elementCount ) );
  350. else
  351. stream.writeText( String::ToString( "%s %s;\r\n", isDeprecated ? "deprecated" : type, field.pFieldname ) );
  352. }
  353. static void dumpClassFooter( Stream &stream )
  354. {
  355. stream.writeText( "};\r\n\r\n" );
  356. }
  357. static void dumpGroupStart( Stream &stream,
  358. const char *aName,
  359. const char *aDocs = NULL )
  360. {
  361. stream.writeText( String::ToString( "\r\n/*! @name %s\r\n", aName ) );
  362. if ( aDocs )
  363. {
  364. stream.writeText( aDocs );
  365. stream.writeText( "\r\n" );
  366. }
  367. stream.writeText( "@{ */\r\n" );
  368. // Add a blank comment in order to make sure groups are parsed properly.
  369. //Con::printf(" /*! */");
  370. }
  371. static void dumpGroupEnd( Stream &stream )
  372. {
  373. stream.writeText( "/// @}\r\n\r\n" );
  374. }
  375. static void dumpClasses( Stream &stream )
  376. {
  377. Namespace::trashCache();
  378. VectorPtr<Namespace*> vec;
  379. vec.reserve( 1024 );
  380. // We use mHashSequence to mark if we have traversed...
  381. // so mark all as zero to start.
  382. for ( Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext )
  383. walk->mHashSequence = 0;
  384. for(Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext)
  385. {
  386. VectorPtr<Namespace*> stack;
  387. stack.reserve( 1024 );
  388. // Get all the parents of this namespace... (and mark them as we go)
  389. Namespace *parentWalk = walk;
  390. while(parentWalk)
  391. {
  392. if(parentWalk->mHashSequence != 0)
  393. break;
  394. if(parentWalk->mPackage == 0)
  395. {
  396. parentWalk->mHashSequence = 1; // Mark as traversed.
  397. stack.push_back(parentWalk);
  398. }
  399. parentWalk = parentWalk->mParent;
  400. }
  401. // Load stack into our results vector.
  402. while(stack.size())
  403. {
  404. vec.push_back(stack[stack.size() - 1]);
  405. stack.pop_back();
  406. }
  407. }
  408. // Go through previously discovered classes
  409. U32 i;
  410. for(i = 0; i < vec.size(); i++)
  411. {
  412. const char *className = vec[i]->mName;
  413. const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
  414. // Skip the global namespace, that gets dealt with in dumpFunctions
  415. if(!className)
  416. continue;
  417. // We're just dumping engine functions, then we don't want to dump
  418. // a class that only contains script functions. So, we iterate over
  419. // all the functions.
  420. bool found = false;
  421. for( Namespace::Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext )
  422. {
  423. if( ewalk->mType != Namespace::Entry::ConsoleFunctionType )
  424. {
  425. found = true;
  426. break;
  427. }
  428. }
  429. // If we don't have engine functions and the namespace name
  430. // doesn't match the class name... then its a script class.
  431. if ( !found && !vec[i]->isClass() )
  432. continue;
  433. // If we hit a class with no members and no classRep, do clever filtering.
  434. if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
  435. {
  436. // Print out a short stub so we get a proper class hierarchy.
  437. if ( superClassName )
  438. {
  439. // Filter hack; we don't want non-inheriting classes...
  440. dumpClassHeader( stream, NULL, className, superClassName );
  441. dumpClassFooter( stream );
  442. }
  443. continue;
  444. }
  445. // Skip over hidden or internal classes.
  446. if( vec[i]->mUsage &&
  447. ( dStrstr( vec[i]->mUsage, "@hide" ) || dStrstr( vec[i]->mUsage, "@internal" ) ) )
  448. continue;
  449. // Print the header for the class..
  450. dumpClassHeader( stream, vec[i]->mUsage, className, superClassName );
  451. // Dump all fragments for this class.
  452. for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; fragment != NULL; fragment = fragment->mNext )
  453. if( fragment->mClass && dStricmp( fragment->mClass, className ) == 0 )
  454. dumpFragment( stream, fragment );
  455. // Dump member functions.
  456. dumpNamespaceEntries( stream, vec[ i ], false );
  457. // Dump callbacks.
  458. dumpGroupStart( stream, "Callbacks" );
  459. dumpNamespaceEntries( stream, vec[ i ], true );
  460. dumpGroupEnd( stream );
  461. // Dump static member variables.
  462. dumpVariables( stream, className );
  463. // Deal with the classRep (to get members)...
  464. AbstractClassRep *rep = vec[i]->mClassRep;
  465. AbstractClassRep::FieldList emptyList;
  466. AbstractClassRep::FieldList *parentList = &emptyList;
  467. AbstractClassRep::FieldList *fieldList = &emptyList;
  468. if ( rep )
  469. {
  470. // Get information about the parent's fields...
  471. AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
  472. if(parentRep)
  473. parentList = &(parentRep->mFieldList);
  474. // Get information about our fields
  475. fieldList = &(rep->mFieldList);
  476. // Go through all our fields...
  477. for(U32 j = 0; j < fieldList->size(); j++)
  478. {
  479. const AbstractClassRep::Field &field = (*fieldList)[j];
  480. switch( field.type )
  481. {
  482. case AbstractClassRep::StartArrayFieldType:
  483. case AbstractClassRep::EndArrayFieldType:
  484. break;
  485. case AbstractClassRep::StartGroupFieldType:
  486. dumpGroupStart( stream, field.pGroupname, field.pFieldDocs );
  487. break;
  488. case AbstractClassRep::EndGroupFieldType:
  489. dumpGroupEnd( stream );
  490. break;
  491. default:
  492. case AbstractClassRep::DeprecatedFieldType:
  493. // Skip over fields that are already defined in
  494. // our parent class.
  495. if ( parentRep && parentRep->findField( field.pFieldname ) )
  496. continue;
  497. dumpClassMember( stream, field );
  498. break;
  499. }
  500. }
  501. }
  502. // Close the class/namespace.
  503. dumpClassFooter( stream );
  504. }
  505. }
  506. static void dumpEnum( Stream& stream, const EngineTypeInfo* type )
  507. {
  508. if( !type->getEnumTable() ) // Sanity check.
  509. return;
  510. // Skip internals... don't export them.
  511. if ( type->getDocString() &&
  512. ( dStrstr( type->getDocString(), "@hide" ) || dStrstr( type->getDocString(), "@internal" ) ) )
  513. return;
  514. // Write documentation.
  515. stream.writeText( "/*!\r\n" );
  516. dumpDoc( stream, type->getDocString() );
  517. stream.writeText( "*/\r\n" );
  518. // Write definition.
  519. stream.writeText( "enum " );
  520. stream.writeText( type->getTypeName() );
  521. stream.writeText( " {\r\n" );
  522. const EngineEnumTable& table = *( type->getEnumTable() );
  523. const U32 numValues = table.getNumValues();
  524. for( U32 i = 0; i < numValues; ++ i )
  525. {
  526. const EngineEnumTable::Value& value = table[ i ];
  527. stream.writeText( "/*!\r\n" );
  528. dumpDoc( stream, value.getDocString(), false );
  529. stream.writeText( "*/\r\n" );
  530. stream.writeText( value.getName() );
  531. stream.writeText( ",\r\n" );
  532. }
  533. stream.writeText( "};\r\n" );
  534. }
  535. static void dumpEnums( Stream& stream )
  536. {
  537. for( const EngineTypeInfo* type = EngineTypeInfo::getFirstType();
  538. type != NULL; type = type->getNextType() )
  539. if( type->isEnum() || type->isBitfield() )
  540. dumpEnum( stream, type );
  541. }
  542. static bool dumpEngineDocs( const char *outputFile )
  543. {
  544. // Create the output stream.
  545. FileStream stream;
  546. if ( !stream.open( outputFile, Torque::FS::File::Write ) )
  547. {
  548. Con::errorf( "dumpEngineDocs - Failed to open output file." );
  549. return false;
  550. }
  551. // First dump all global ConsoleDoc fragments.
  552. for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; fragment != NULL; fragment = fragment->mNext )
  553. if( !fragment->mClass )
  554. dumpFragment( stream, fragment );
  555. // Clear the doc groups before continuing,
  556. smDocGroups.clear();
  557. // Dump enumeration types.
  558. dumpEnums( stream );
  559. // Dump all global variables.
  560. dumpVariables( stream );
  561. // Now dump the global functions.
  562. Namespace *g = Namespace::find( NULL );
  563. while( g )
  564. {
  565. dumpNamespaceEntries( stream, g );
  566. // Dump callbacks.
  567. dumpGroupStart( stream, "Callbacks" );
  568. dumpNamespaceEntries( stream, g, true );
  569. dumpGroupEnd( stream );
  570. g = g->mParent;
  571. }
  572. // Now dump all the classes.
  573. dumpClasses( stream );
  574. // Dump pre-declarations for any groups we encountered
  575. // so that we don't have to explicitly define them.
  576. HashTable<String,U32>::Iterator iter = smDocGroups.begin();
  577. for (; iter != smDocGroups.end(); ++iter)
  578. stream.writeText( String::ToString( "/*! @addtogroup %s */\r\n\r\n", iter->key.c_str() ) );
  579. return true;
  580. }
  581. DefineEngineFunction( dumpEngineDocs, bool, ( const char* outputFile ),,
  582. "Dumps the engine scripting documentation to the specified file overwriting any existing content.\n"
  583. "@param outputFile The relative or absolute output file path and name.\n"
  584. "@return Returns true if successful.\n"
  585. "@ingroup Console")
  586. {
  587. return dumpEngineDocs( outputFile );
  588. }