Just a little something to generate nicer module documentation. It's not done yet, so you'll see some stuff missing. Use it if you like, and feel free to add to it.
It eats your mod folder and spits out a document folder :)
Warning: It overwrites your original BlitzMax Command Reference. Use at your own risk!
It spits out html files, looking something like this:

Have Fun!
It eats your mod folder and spits out a document folder :)
Warning: It overwrites your original BlitzMax Command Reference. Use at your own risk!
' ' Documenta.bmx ' BlitzMax Module Documentation Generator ' ' Version: 0.77 (with modifications from Perturbatio) ' Purpose: To create nice help files for BlitzMax. ' Author: Mikkel Fredborg ' ' Warning: Use this at your own risk! It will overwrite your original BlitzMax ' Command Reference. Please make sure to back up the documents before ' running Documenta! ' ' ToDo: Add better (less hardcoded) css formatting (and make it prettier). ' Add the ability to import additional comments files. ' Comment code :) ' ' History: ' 0.77 - automatically retrieve bmax folder so that it will run without need to configure ' 0.76 - Added url to module source so that the IDE can open it (PERTURBATIO) ' 0.75 - Relative file paths ' 0.71 - Compatible with BlitzMax 1.03 ' 0.7 - Converts language reference and user guide! ' 0.65 - Minor tweaks ' 0.6 - Navbar works with Firefox ' Overwrites original Blitzmax module documentation!!! ' 0.5 - Auto fixes table layout/style ' Fix Intro formatting ' 0.4 - Recognizes Global function definitions (ie. Global AppArgs$[]="bbAppArgs") ' 0.3 - Skip Private and Extern sections ' Exclude modules which doesn't have any functions ' 0.2 - Examples and Module introductions are included ' 0.1 - Basic stuff ' ' ' Change this to your BlitzMax folder Global bmaxfolder:String = getenv_("BMXPATH") ' ' Modify these if you want extra stuff Const SKIP_PRIVATE = True ' Skips commands private to a module Const SKIP_EXTERN = False ' Skip extern commands Const SKIP_NONDOCED = False ' Skip non documented commands ' ' Fix up path if it isn't slashed bmaxfolder = StripSlash(bmaxfolder)+"/" bmaxfolder = bmaxfolder.Replace("","/") If FileType(StripSlash(bmaxfolder)) = 0 Notify "Incorrect BlitzMax folder!~n~q"+bmaxfolder+"~q!" End EndIf ' ' Destination folder...only works correctly if it's the bmax folder :) Global destfolder:String = bmaxfolder ' ' Warn if overwriting If bmaxfolder = destfolder If Proceed("Warning!~n~nThis will overwrite some of the original BlitzMax help files.~nPlease make sure you have a backup of your 'BlitzMax/doc/bmxmods' folder!~n~nDo you wish to continue?",True)<>1 Print "User aborted...What a sissy :)" End EndIf EndIf ' ' Do NOT modify Const linefeed:String = "~r~n" Const htmlext:String = ".html" Const backuppre:String = "documenta_backup_" Global modlist:TList = New TList Global funlist:TList = New TList Global CSS_List:TList = New TList Type dmod Field docfile:String Field file:String Field name:String Field info:String Field intro:String Field funclist:TList = New TList Method New() modlist.addlast Self End Method Method AddFunction:dfunc() Local f:dfunc = New dfunc f.mymod = Self Self.funclist.addlast f funlist.addlast f Return f End Method Method compare:Int(otherObj:Object) Local m:dmod = dmod(otherObj) If Not m Then Return 1 Return Self.name.compare(m.name) End Method End Type Type dfunc Field name:String Field sname:String Field param:String Field desc:String Field rets:String Field about:String Field example:String Field exfile:String Field mymod:dmod Field keyword:Int Method compare:Int(otherObj:Object) Local f:dfunc = dfunc(otherObj) If Not f Then Return 1 Return Self.sname.compare(f.sname) End Method End Type ' ' Converted from: ' <a href="http://www.codeguru.com/Cpp/misc/misc/fileanddirectorynaming/article.php/c263/" target="_blank">www.codeguru.com/Cpp/misc/misc/fileanddirectorynaming/article.php/c263/</a> ' Function GetRelativeFilename:String(currentDirectory:String, absoluteFilename:String) Local slash = Asc("/") currentDirectory = currentDirectory.Replace("","/") absoluteFilename = absoluteFilename.Replace("","/") If currentDirectory[currentDirectory.length-1]<>slash currentDirectory:+"/" EndIf Local afMarker:Int = 0 Local rfMarker:Int = 0 Local cdLen:Int = 0 Local afLen:Int = 0 Local i:Int = 0 Local levels:Int = 0 Local relativeFilename:String cdLen = currentDirectory.length afLen = absoluteFilename.length ' Handle DOS names that are on different drives: If (Lower(Chr(currentDirectory[0])) <> Lower(Chr(absoluteFilename[0]))) ' Not on the same drive, so only absolute filename will do relativeFilename = absoluteFilename Return relativeFilename EndIf ' they are on the same drive, find out how much of the current directory ' is in the absolute filename i = 3 While((i < afLen) And (i < cdLen) And (Lower(Chr(currentDirectory[i])) = Lower(Chr(absoluteFilename[i])))) i:+1 Wend If (i = cdLen And (absoluteFilename[i] = slash Or absoluteFilename[i-1] = slash)) ' the whole current directory name is in the file name, ' so we just trim off the current directory name to get the ' current file name. If(absoluteFilename[i] = slash) ' a directory name might have a trailing slash but a relative ' file name should not have a leading one... i:+1 EndIf relativeFilename = absoluteFilename[i..afLen] Return relativeFilename EndIf ' The file is not in a child directory of the current directory, so we ' need to step back the appropriate number of parent directories by ' using ".."s. First find out how many levels deeper we are than the ' common directory afMarker = i; levels = 1; ' count the number of directory levels we have to go up to get to the ' common directory While(i < cdLen) i:+1 If(currentDirectory[i] = slash) ' make sure it's not a trailing slash i:+1 If i<cdLen'(currentDirectory[i] != '\0') levels:+1 EndIf EndIf Wend ' move the absolute filename marker back to the start of the directory name ' that it has stopped in. While(afMarker > 0 And absoluteFilename[afMarker-1] <> slash) afMarker:-1 Wend ' add the appropriate number of "../"s. rfMarker = 0; For i = 0 Until levels relativeFilename:+"../" Next ' copy the rest of the filename into the result string relativeFilename:+absoluteFilename[afMarker..afLen] Return relativeFilename End Function ' ' Scans the BlitzMax module folder to find all modules Function ScanModFolder(f:String) f = StripSlash(f) Local files:String[] files = LoadDir(f,True) ' ' Find module files For Local fi:String = EachIn files If KeyDown(KEY_ESCAPE) Then Exit Local file:String = f+"/"+fi If FileType(file)=1 If Lower(fi[fi.length-4..fi.length])=".bmx" ScanBMXFile(file) EndIf Else ScanModFolder(file) EndIf FlushMem Next End Function ' ' Scans a .bmx file and extracts info from modules Function ScanBMXFile(file:String) Local m:dmod Local bbdoc:String Local returns:String Local about:String Local scanmode:Int = 0 Local keyword:Int = False Local inextern:Int = False Local inprivate:Int = False Local temp:String Local temp2:String Local f1:Int Local f2:Int Local stream = ReadFile(file) If stream While Not Eof(stream) l:String = ReadLine(stream) If SKIP_EXTERN If Lower(l[0..6]) = "extern" inextern = True EndIf If Lower(l[0..9]) = "endextern" Or Lower(l[0..10]) = "end extern" inextern = False EndIf EndIf If SKIP_PRIVATE If Lower(l[0..7]) = "private" inprivate = True EndIf If Lower(l[0..6]) = "public" inprivate = False EndIf EndIf If inextern = False And inprivate = False If m = Null ' ' module name If Lower(l[0..7]) = "module " m = New dmod m.file = file m.name = FormatString(l[7..l.length]) m.info = "<tr><td class=~qmodinfoleft~q>Module</td><td class=~qmodinforight~q>"+m.name+"</td></tr>"+linefeed EndIf Else ' ' Module info If Lower(l[0..10]) = "moduleinfo" f1 = l.find("~q",0)+1 f2 = l.find("~q",f1) If f1>0 And f2>0 temp = l[f1..f2] f1 = temp.find(":",0) If f1 temp2 = FormatString(temp[0..f1]) temp = FormatString(temp[f1+1..temp.length]) EndIf m.info = m.info+"<tr><td class=~qmodinfoleft~q>"+temp2+"</td><td class=~qmodinforight~q>"+temp+"</td></tr>"+linefeed EndIf EndIf Local ol = l.length If Lower(l[0..6])="bbdoc:" l = l[6..l.length] scanmode = 1 EndIf If Lower(l[0..8])="returns:" l = l[8..l.length] scanmode = 2 EndIf If Lower(l[0..6])="about:" l = l[6..l.length] scanmode = 3 EndIf If scanmode If Lower(l[0..6])="endrem" Or Lower(l[0..7])="end rem" Or ol < 2 scanmode = False Else If scanmode = 1 bbdoc = bbdoc+FormatString(l,True)+" " If scanmode = 2 returns = returns+FormatString(l,True)+" " If scanmode = 3 about = about+FormatString(l,True)+" " EndIf EndIf If keyword If f:dfunc f.desc = bbdoc f.rets = returns f.about = about EndIf EndIf If Lower(l[0..7])="global " If bbdoc = "" l = ".."+l EndIf EndIf If Lower(l[0..9])="function " Or Lower(l[0..9])="keyword: " Or Lower(l[0..7])="global " f:dfunc = m.addfunction() If Lower(l[0..9])="function " f.desc = bbdoc f.rets = returns f.about = about keyword = False f1 = l.find("(") f.name = l[9..f1] f1 = f1+1 f2 = l.length Local level = 1 For Local i = f1 Until l.length If l[i] = Asc("(") Then level:+1 If l[i] = Asc(")") Then level:-1 If level = 0 f2 = i Exit EndIf Next f.param = l[f1..f2] ElseIf Lower(l[0..9])="keyword: " keyword = True f.name = l[9..l.length] f.name = f.name.Replace("~q","") f.name = f.name.Trim() f.keyword = True Else keyword = False f.name = l[7..l.find("=")]+"()" f.name = f.name.Trim() f.keyword = True f.desc = bbdoc f.rets = returns f.about = about keyword = False EndIf bbdoc = "" returns = "" about = "" f.sname = f.name For i = 0 Until f.sname.length If f.sname[i] = Asc("$") Or f.sname[i] = Asc("#") Or f.sname[i] = Asc("%") Or f.sname[i] = Asc(":") Or f.sname[i] = Asc("!") f.sname = f.sname[0..i] Exit EndIf Next EndIf EndIf EndIf Wend CloseFile(stream) EndIf If m <> Null Print file EndIf Return m<>Null End Function ' ' Formats a string with links, styles etc. Function FormatString:String(s:String,aboutmode:Int=False) Local f1:Int,f2:Int,i:Int Local link:String ' ' Remove non printable characters before and after s = s.Trim() ' ' Remove starting break If Lower(s[0..4]) = "<br>" s = s[4..s.length] s = s.Trim() EndIf ' ' We don't like <p> s = s.Replace("<p>","<br><br>") s = s.Replace("</p>","<br><br>") ' ' Fix tables s = s.Replace("<table>","<table class=~qsubtable~q>") s = s.Replace("<th>","<th class=~qsubth~q>") s = s.Replace("<td>","<td class=~qsubtd~q>") ' ' Auto fix http:// links f1 = s.find("http://") f2 = s.length If f1=>0 For i = f1 Until s.length If s[i]<=32 f2 = i Exit EndIf Next link:String = s[f1..f2] s = s[0..f1]+"<a href=~q"+link+"~q target=~q_blank~q>"+link+"</a>"+s[f2..s.length] EndIf If aboutmode ' ' bbdoc #something = link to another function Repeat f1 = s.find("#") f2 = s.length If f1=>0 For i = f1 Until s.length If s[i]<=32 Or s[i]=Asc(",") Or s[i]=Asc(".") Or s[i]=Asc(")") Or s[i]=Asc("(") f2 = i Exit EndIf Next link:String = s[f1+1..f2] s = s[0..f1]+"<a href=~q***"+link+"~q>"+link+"</a>"+s[f2..s.length] EndIf Until f1<0 ' ' bbdoc @something = Parameter description Repeat f1 = s.find("@") f2 = s.length If f1=>0 For i = f1 Until s.length If s[i]<=32 Or s[i]=Asc(",") Or s[i]=Asc(".") Or s[i]=Asc(")") Or s[i]=Asc("(") f2 = i Exit EndIf Next link:String = s[f1+1..f2] s = s[0..f1]+"<font class=~qfuncparam~q>"+link+"</font>"+s[f2..s.length] EndIf Until f1<0 EndIf Return s End Function ' ' Fixes up HTML files to look nicer Function ReformatHTML:String(s:String,isIntro:Int=False) ' Remove header s = RemoveSection(s,"<head>","</head>") ' ' Fix tables s = ReplaceSection(s,"<table",">","<table class=~qsubtable~q>") s = ReplaceSection(s,"<th",">","<th class=~qsubth~q>") s = ReplaceSection(s,"<td",">","<td class=~qsubtd~q>") If isIntro ' Remove original title s = RemoveSection(s,"<h1>","</h1>") ' ' Add some stuff :) s = "<h2>Introduction</h2>"+s Else ' ' Fix title s = s.Replace("<h1>","<tr><td class=~qmodulename~q>") s = s.Replace("</h1>","</td></tr><tr><td class=~qmoduleintro~q>") EndIf ' Remove html and body tags s = s.Replace("<html>","") s = s.Replace("</html>","") s = s.Replace("<body>","") s = s.Replace("</body>","") s = s.Trim() ' ' We don't like <p> s = s.Replace("<p>","<br><br>") s = s.Replace("</p>","<br><br>") ' ' Replace styling s = s.Replace("class=syntax","class=~qfuncparam~q") s = s.Replace("class=token","class=~qfuncname~q") ' ' Fix headings s = s.Replace("<h2>","</td></tr><tr><td class=~qmoduleintrohead~q>") s = s.Replace("</h2>","</td></tr><tr><td class=~qmoduleintro~q>") Return s End Function ' ' Fixes up navbar.html to look nicer Function ReformatNavbar:String(s:String) ' Remove header s = RemoveSection(s,"<head>","</head>") ' Remove html and body tags s = s.Replace("<html>","") s = s.Replace("</html>","") s = s.Replace("<body>","") s = s.Replace("</body>","") s = s.Trim() ' Replace <bold> tag s = s.Replace("<b>","<div class=~qnav~q>") s = s.Replace("</b>","</div>") ' Remove <br> tags s = s.Replace("<br>","") ' fix styles s = s.Replace("class=null","class=~qnavmodlink~q") s = s.Replace("<a","<div class=~qnavmod~q><a") s = s.Replace("</a>","</a></div>") Return s End Function ' ' Removes a section of string 's' between 'a' and 'b'. ' If 'all' is true 'a' and 'b' is removed as well. Function RemoveSection:String(s:String,a:String,b:String,all:Int=True) Local f1 = s.find(a) If f1=>0 Local f2 = s.find(b,f1) If f2=>0 If all s = s[0..f1]+s[f2+b.length..s.length] Else s = s[0..f1+a.length]+s[f2..s.length] EndIf EndIf EndIf Return s End Function Function ReplaceSection:String(s:String,a:String,b:String,repl:String) Local f1 = 0 While f1=>0 f1 = s.find(a,f1) If f1=>0 Local f2 = s.find(b,f1) If f2=>0 s = s[0..f1]+repl+s[f2+b.length..s.length] EndIf f1:+1 EndIf Wend Return s End Function ' ' Fixes html links to functions/commands Function FixFunctionRefs:String(s:String,m:dmod) Local f1 = 0 While f1=>0 f1 = s.find("***",f1) If f1=>0 Local f2 = s.find("~q",f1+3) If f2=>0 Local funname:String = s[f1+3..f2] Local found = False ' ' Check the same mod first For ff:dfunc = EachIn m.funclist If Lower(ff.sname) = Lower(funname) s = s[0..f1]+"#"+ff.sname+s[f2..s.length] found = True EndIf Next If found = False ' ' Check other mods, if the reference is not within the same mod For ff:dfunc = EachIn funlist If Lower(ff.sname) = Lower(funname) relFile:String = GetRelativeFilename(ExtractDir(m.docfile),ff.mymod.docfile) s = s[0..f1]+relFile+"#"+ff.sname+s[f2..s.length] EndIf Next EndIf EndIf f1 = f1+1 EndIf Wend Return s End Function ' ' Finds Example source files for commands, and intro.html files ' if they exist for the different modules and functions. Function FindExamples(modsfolder:String) For m:dmod = EachIn modlist Local intro:String = Lower(ExtractDir(m.file)+"/doc/intro.html") Local stream = ReadFile(intro) If stream Local size = StreamSize(stream) If size>0 m.intro = ReadString(stream,size) m.intro = ReformatHTML(m.intro,True) Print m.name+" -> introduction found!" EndIf CloseFile stream EndIf FlushMem Next For f:dfunc = EachIn funlist Local example:String = Lower(ExtractDir(f.mymod.file)+"/doc/"+f.sname+".bmx") stream = ReadFile(example) If stream size = StreamSize(stream) If size>0 f.exfile = example f.example = ReadString(stream,size) Print f.sname+" -> example found!" EndIf CloseFile stream EndIf FlushMem Next End Function ' ' Writes the module documents Function WriteModDocs() Local stream Local modsfolder:String = bmaxfolder+"mod" Local outfolder:String = destfolder+"doc/bmxmods" outfolder = StripSlash(outfolder) If FileType(outfolder) = 0 Assert CreateDir(outfolder),"Couldn't create destination folder!" EndIf Print "Scanning for modules..." ScanModFolder(modsfolder) Print "Scanning for examples..." FindExamples(modsfolder) For m:dmod = EachIn modlist m.docfile:String = Lower(ExtractDir(m.file)+"/doc/commands.html") Next ' ' Remove commands with no documentation If SKIP_NONDOCED For f:dfunc = EachIn funlist If f.rets.length = 0 And f.desc.length = 0 And f.about.length = 0 ListRemove(f.mymod.funclist,f) ListRemove(funlist,f) EndIf Next EndIf ' ' Sort out cross references For f:dfunc = EachIn funlist f.about = FixFunctionRefs(f.about,f.mymod) Next For m:dmod = EachIn modlist m.intro = FixFunctionRefs(m.intro,m) Next modlist.sort Print modlist.count()+" Modules found!" Print "Writing Commands.txt..." stream = WriteFile(bmaxfolder+"doc/bmxmods/commands.txt") If stream For f:dfunc = EachIn funlist relFile:String = f.mymod.docfile'GetRelativeFilename(bmaxfolder,f.mymod.docfile) relFile = relFile[Len(StripSlash(bmaxfolder))..] If f.keyword = False Print "RELFILE:"+relFile WriteLine(stream,f.name+"("+f.param+")|"+relFile+"#"+f.sname) Else WriteLine(stream,f.name+"|"+relFile+"#"+f.sname) EndIf Next CloseFile stream Else Notify "Couldn't write commands.txt" EndIf Print "Writing Module Documents..." ' ' Write module doc files For m:dmod = EachIn modlist destdir:String = ExtractDir(m.docfile) If FileType(destdir) = 0 If CreateDir(destdir) = False Notify("Couldn't create 'doc/' folder for module:~n~q"+m.name+"~q") EndIf EndIf stream = WriteFile(m.docfile) If stream ' html header WriteLine(stream,"<html>") WriteLine(stream,"<head>") relFile:String = GetRelativeFilename(ExtractDir(m.docfile),bmaxfolder+"doc/bmxmods/docs.css") WriteLine(stream,"<link rel=styleSheet href=~q"+relFile+"~q type='text/css'>") WriteLine(stream,"</head>") WriteLine(stream,"<body class=~qbody~q>") ' module name WriteLine(stream,"<table width=100% class=~qbody~q>") WriteLine(stream,"<tr><td class=~qmodulename~q><a href=~qfile://" + m.file +"~q> "+m.name+"</a></td></tr>") ' module intro WriteLine(stream,"<tr><td class=~qmoduleintro~q>"+m.intro+"</td></tr>") ' module functions If m.funclist.count()>0 m.funclist.sort WriteLine(stream,"<tr><td class=~qbody~q>") For f:dfunc = EachIn m.funclist WriteLine(stream,"<table class=~qfunctionbox~q><tr><td class=~qfunchead~q colspan=2 id=~q"+f.sname+"~q>") If f.keyword = False WriteLine(stream,f.name+"(<font class=~qfuncparam~q>"+f.param+"</font>)") Else WriteLine(stream,f.name) EndIf WriteLine(stream,"</td></tr>") If f.rets.length WriteLine(stream,"<tr><td class=~qfuncleft~q>Returns</td>") WriteLine(stream,"<td class=~qfuncright~q>"+f.rets+"</td></tr>") EndIf If f.desc.length WriteLine(stream,"<tr><td class=~qfuncleft~q>Short description</td>") WriteLine(stream,"<td class=~qfuncright~q>"+f.desc+"</td></tr>") EndIf If f.about.length WriteLine(stream,"<tr><td class=~qfuncleft~q>Long description</td>") WriteLine(stream,"<td class=~qfuncright~q>"+f.about+"</td></tr>") EndIf If f.example.length WriteLine(stream,"<tr><td class=~qfuncleft~q>Example</td>") relFile:String = GetRelativeFilename(ExtractDir(m.docfile),f.exfile) WriteLine(stream,"<td class=~qfuncright~q><a href=~q"+relFile+"~q target=~q_blank~q class=~qexample~q><pre>"+f.example+"</pre></a></td></tr>") EndIf ' ' Remember to fetch examples??? WriteLine(stream,"</table><br>") Next WriteLine stream,"</td></tr>" EndIf ' module info WriteLine(stream,"<tr><td class=~qbody~q>") Local out:String = "<table class=~qmodinfobox~q>"+linefeed+"<tr><td colspan=2 class=~qmodinfohead~q>Module Info</tr></td>"+linefeed+m.info+linefeed+"</table>" WriteLine(stream,out) WriteLine(stream,"</td></tr></table>") WriteLine(stream,"</body>") WriteLine(stream,"</html>") CloseFile stream Else Notify "Couldn't create output file:~n~q"+m.docfile+"~q" EndIf Next Print "Writing Navigation Document..." ' ' Write navigation file Local navfile:String = bmaxfolder+"doc/bmxmods/navbar.html" stream = WriteFile(navfile) If stream ' html header WriteLine(stream,"<html>") WriteLine(stream,"<head>") WriteLine(stream,"<link rel=styleSheet href=~qdocs.css~q type='text/css'>") WriteLine(stream,"<script>") WriteLine(stream,"// Do we use DOM or not?") WriteLine(stream,"var dom = (document.getElementById && !document.all)? 1: 0;") WriteLine(stream,"function toggle(the_id)") WriteLine(stream,"{") WriteLine(stream," var obj = (dom)? document.getElementById(the_id): document.all[the_id];") WriteLine(stream," if(obj.style.display == 'inline'){") WriteLine(stream," obj.style.visibility = 'hidden';") WriteLine(stream," obj.style.display = 'none';") WriteLine(stream," }else{") WriteLine(stream," obj.style.visibility = 'visible';") WriteLine(stream," obj.style.display = 'inline';") WriteLine(stream," }") WriteLine(stream,"}") WriteLine(stream,"</script>") WriteLine(stream,"</head>") WriteLine(stream,"<body>") ' module list WriteLine(stream,"<table width=~q200px~q><tr><td>") WriteLine(stream,"<div class=~qnav~q>By Module</div>") For m:dmod = EachIn modlist Local id:String = m.name.Replace(".","_") Local first = True For f:dfunc = EachIn m.funclist If first relFile:String = GetRelativeFilename(ExtractDir(navfile),m.docfile) WriteLine(stream,"<div class=~qnavmod~q><a class=~qnavmodlink~q onClick=~qtoggle('"+id+"')~q href=~q"+relFile+"~q target=~qmain~q>"+m.name+"</a></div>") WriteLine(stream,"<div id=~q"+id+"~q class=~qnavfunclist~q>") first = False EndIf WriteLine(stream,"<div class=~qnavfunc~q><a class=~qnavfunclink~q href=~q"+relFile+"#"+f.sname+"~q target=~qmain~q>"+f.sname+"</a></div>") Next If first = False WriteLine(stream,"</div>") EndIf Next WriteLine(stream,"</td></tr><tr><td>") ' alphabetical function list WriteLine(stream,"<div class=~qnav~q><br>Alphabetical</div>") funlist.sort alp:String = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ" For Local char:Int = 0 Until alp.length s:String = Chr(alp[char]) id:String = "Alpha_"+s first = True For f:dfunc = EachIn funlist If Upper(Chr(f.sname[0]))=s If first WriteLine(stream,"<div class=~qnavmod~q><a class=~qnavmodlink~q href=~qjavascript:toggle('"+id+"')~q>"+s+"</a></div>") WriteLine(stream,"<div id=~q"+id+"~q class=~qnavfunclist~q>") first = False EndIf relFile:String = GetRelativeFilename(ExtractDir(navfile),f.mymod.docfile) WriteLine(stream,"<div class=~qnavfunc~q><a class=~qnavfunclink~q href=~q"+relFile+"#"+f.sname+"~q target=~qmain~q>"+f.sname+"</a></div>") EndIf Next If first = False WriteLine(stream,"</div>") EndIf Next WriteLine(stream,"</td></tr></table>") WriteLine(stream,"</body>") WriteLine(stream,"</html>") CloseFile stream Else Notify "Couldn't create output file:~n~q"+navfile+"~q" EndIf WriteIndexFile(bmaxfolder+"doc/bmxmods/index.html") GenerateCSSFile(outfolder) End Function ' ' Write index.html file Function WriteIndexFile(indfile:String) Print "Writing Index Document..." stream = WriteFile(indfile) If stream WriteLine(stream,"<html><head>") WriteLine(stream,"<title>BlitzMax Command Reference</title>") WriteLine(stream,"</head>") WriteLine(stream,"<frameset cols='230,*'>") WriteLine(stream,"<frame src=~qnavbar.html~q name=~qnavbar~q><frame src=~qwelcome.html~q name=~qmain~q>") WriteLine(stream,"</frameset>") WriteLine(stream,"</html>") CloseFile stream Else Notify "Couldn't create output file:~n~q"+indfile+"~q" EndIf End Function ' ' Converts non module documents to the same style Function ConvertDocs() Local checkfolder:String[] = [bmaxfolder+"doc/bmxmods",bmaxfolder+"doc/bmxlang",bmaxfolder+"doc/bmxuser"] Local stream Local size Local folder:String Local files:String[] Local file:String Local fi:String Local s:String For folder = EachIn checkfolder ' ' Backup original html files BackupFolder(folder,["htm","html"]) files = LoadDir(folder,True) For fi = EachIn files file = folder+"/"+fi If FileType(file)=1 ' ' This file is an original html file that's ' been backed up. We use that to generate the new ' docs. If file.find(backuppre)=>0 Local f1:Int = file.find(backuppre) Local targetfile:String = file[0..f1]+file[f1+backuppre.length..file.length] stream = ReadFile(file) If stream size = StreamSize(stream) If size>0 s:String = ReadString(stream,size) EndIf CloseFile stream If targetfile.find("navbar")=>0 ' Navigation bar s = ReformatNavbar(s) stream = WriteFile(targetfile) If stream ' html header WriteLine(stream,"<html>") WriteLine(stream,"<head>") WriteLine(stream,"<link rel=styleSheet href=~qdocs.css~q type='text/css'>") WriteLine(stream,"<script>") WriteLine(stream,"// Do we use DOM or not?") WriteLine(stream,"var dom = (document.getElementById && !document.all)? 1: 0;") WriteLine(stream,"function toggle(the_id)") WriteLine(stream,"{") WriteLine(stream," var obj = (dom)? document.getElementById(the_id): document.all[the_id];") WriteLine(stream," if(obj.style.display == 'inline'){") WriteLine(stream," obj.style.visibility = 'hidden';") WriteLine(stream," obj.style.display = 'none';") WriteLine(stream," }else{") WriteLine(stream," obj.style.visibility = 'visible';") WriteLine(stream," obj.style.display = 'inline';") WriteLine(stream," }") WriteLine(stream,"}") WriteLine(stream,"</script>") WriteLine(stream,"</head>") WriteLine(stream,"<body>") ' nav list WriteLine(stream,"<table width=~q200px~q><tr><td>") WriteLine(stream,s) WriteLine(stream,"</td></tr></table>") WriteLine(stream,"</body>") WriteLine(stream,"</html>") CloseFile stream Else Notify "Unable to write file:~n~q"+targetfile+"~q!",True End EndIf Else ' Regular html doc s = ReformatHTML(s) stream = WriteFile(targetfile) If stream ' html header WriteLine(stream,"<html>") WriteLine(stream,"<head>") WriteLine(stream,"<link rel=styleSheet href=~qfile://"+folder+"/docs.css~q type='text/css'>") WriteLine(stream,"</head>") WriteLine(stream,"<body class=~qbody~q>") WriteLine(stream,"<table width=95% class=~qbody~q>") WriteLine(stream,"<tr><td class=~qmoduleintro~q>") WriteString(stream,s) WriteLine(stream,"</td></tr></table>") WriteLine(stream,"</body>") WriteLine(stream,"</html>") CloseFile stream Else Notify "Unable to write file:~n~q"+targetfile+"~q!",True End EndIf EndIf EndIf EndIf EndIf Next GenerateCSSFile(folder) WriteIndexFile(folder+"/index.html") Next End Function ' ' Backups all files with certain extensions in a folder Function BackupFolder(folder:String,extensions:String[]) Local backitup:Int Local fi:String Local file:String Local ext:String Local files:String[] files = LoadDir(folder,True) For fi = EachIn files file = folder+"/"+fi If FileType(file)=1 If file.find(backuppre)<0 backitup = False If extensions.length = 0 backitup = True EndIf For ext = EachIn extensions If Lower(ExtractExt(file)) = ext backitup = True EndIf Next If backitup If BackupFile(file) Print "Backing up: ~q"+file+"~q..." EndIf EndIf EndIf EndIf FlushMem Next End Function ' ' Backups a single file, only if a backup doesn't already exist, ' unless the force flag is set Function BackupFile(file:String,force:Int=False) Local backupfile:String = ExtractDir(file)+"/"+backuppre+StripDir(file) If FileType(backupfile) = 0 Or force = True Local stream = ReadFile(file) If stream Local size = StreamSize(stream) If size>0 Local s:String = ReadString(stream,size) EndIf CloseFile stream stream = WriteFile(backupfile) If stream WriteString(stream,s) CloseFile stream Else Notify "Unable to backup file:~n~q"+file+"~q!",True End EndIf Else Return False EndIf Else Return False EndIf Return True End Function ' ' Creates a CSS file containing a style sheet Function GenerateCSSFile(outfolder:String) Print "Writing CSS Document..." Local c:CSS_Class ClearList(CSS_List) Local cssfile:String = outfolder+"/docs.css" stream = WriteFile(cssfile) If stream c:CSS_Class = New CSS_Class c.name = ".body" c.background = "#FFFFFF" c.border = "0px" c.font = "10pt helvetica" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".modulename" c.font = "20pt helvetica" c.background = "#FFFFFF" c.border_bottom = "20px solid #FFFFFF" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".moduleintro" c.font = "10pt helvetica" c.background = "#FFFFFF" c.border_bottom = "20px solid #FFFFFF" c.padding_left = "20px" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".moduleintrohead" c.font = "15pt helvetica" c.padding_left = "0px" c:CSS_Class = New CSS_Class c.name = ".functionbox" c.padding_top = "20px" c.font = "10pt helvetica" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".funchead" c.padding = "10px" c.background = "#CCDDEE" c.border_bottom = "1px Solid #BBCCDD" c.font_weight = "bold" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".funcname" c.font_style = "normal" c.font_weight = "bold" c:CSS_Class = New CSS_Class c.name = ".funcparam" c.font_style = "italic" c.font_weight = "normal" c:CSS_Class = New CSS_Class c.name = ".funcleft" c.font_weight = "normal" c.padding = "10px" c.font = "8pt helvetica" c.background = "#CCDDEE" c.border_bottom = "1px solid #BBCCDD" c.width = "15%" c.v_align = "text-top" c:CSS_Class = New CSS_Class c.name = ".funcright" c.font = "10pt helvetica" c.font_style = "normal" c.font_weight = "normal" c.background = "#DDEEFF" c.border_bottom = "1px solid #CCDDEE" c.padding = "10px" c.width = "85%" ' ' ' Module info box c:CSS_Class = New CSS_Class c.name = ".modinfobox" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".modinfohead" c.background = "#CCDDEE" c.border_bottom = "1px solid #BBCCDD" c.font = "8pt helvetica" c.padding = "10px" c.font = "10pt helvetica" c.font_weight = "bold" c.width = "100%" c:CSS_Class = New CSS_Class c.name = ".modinfoleft" c.background = "#CCDDEE" c.border_bottom = "1px solid #BBCCDD" c.font = "8pt helvetica" c.padding_left = "10px" c.width = "15%" c:CSS_Class = New CSS_Class c.name = ".modinforight" c.background = "#DDEEFF" c.font = "8pt helvetica" c.border_bottom = "1px solid #CCDDEE" c.padding_left = "10px" c.width = "85%" ' ' ' Navigation Bar c:CSS_Class = New CSS_Class c.name = ".nav" c.font = "10pt helvetica" c.width = "100%" c.border_top = "2px solid #FFFFFF" c.padding = "0px" c:CSS_Class = New CSS_Class c.name = ".navfunclist" c.display = "none" c.visibility = "hidden" c.font = "10pt helvetica" c.border = "0px" c:CSS_Class = New CSS_Class c.name = ".navmod" c.background = "#CCDDEE" c.border_top = "2px solid #FFFFFF" c.border_bottom = "1px solid #BBCCDD" c.font = "10pt helvetica" c.padding_left = "10px" c:CSS_Class = New CSS_Class c.name = ".navfunc" c.padding_left = "20px" c.background = "#DDEEFF" c.border_top = "2px solid #FFFFFF" c.border_bottom = "1px solid #CCDDEE" ' ' Table stuff c:CSS_Class = New CSS_Class c.name = ".subtable" c.font = "10pt helvetica" c.background = "#FFFFFF" c:CSS_Class = New CSS_Class c.name = ".subth" c.font = "10pt helvetica" c.font_weight = "bold" c.text_align = "left" c.background = "#CCDDEE" c.border_bottom = "1px solid #BBCCDD" c.padding_left = "10px" c.padding_right = "10px" c:CSS_Class = New CSS_Class c.name = ".subtd" c.font = "10pt helvetica" c.font_weight = "normal" c.text_align = "left" c.background = "#DDEEFF" c.border_top = "0px solid #FFFFFF" c.border_bottom = "1px solid #CCDDEE" c.padding_left = "10px" c.padding_right = "10px" ' ' Hyperlink stuff c:CSS_Class = New CSS_Class c.name = "a" c.color = "#000000" c:CSS_Class = New CSS_Class c.name = "a:hover" c.color = "#445566" c:CSS_Class = New CSS_Class c.name = "a.navmodlink" c.font_weight = "bold" c.width = "100%" c.text_decoration = "none" c:CSS_Class = New CSS_Class c.name = "a.navfunclink" c.text_decoration = "none" c.width = "100%" c:CSS_Class = New CSS_Class c.name = "a.example" c.color = "#000000" c.text_decoration = "none" c.width = "100%" c:CSS_Class = New CSS_Class c.name = "pre" c.padding = "10px" c.background = "#EEF8FF" For c:CSS_Class = EachIn CSS_List c.Write(stream) Next CloseFile stream Else Notify "Couldn't create output file:~n~q"+cssfile+"~q" EndIf End Function ' ' CSS_Class type, utillity class to ease writing of css files Type CSS_Class Field name:String Field display:String Field background:String Field cell_spacing:String Field border:String Field border_bottom:String Field border_left:String Field border_top:String Field border_right:String Field color:String Field padding:String Field padding_left:String Field padding_right:String Field padding_top:String Field padding_bottom:String Field font:String Field font_style:String Field font_weight:String Field text_decoration:String Field width:String Field max_width:String Field min_width:String Field height:String Field max_height:String Field min_height:String Field v_align:String Field text_align:String Field visibility:String Method New() CSS_List.addlast Self End Method Method Write(stream) WriteLine(stream,Self.name+" {") If Self.visibility.length Then WriteLine(stream," visibility: "+Self.visibility+";") If Self.display.length Then WriteLine(stream," display: "+Self.display+";") If Self.color.length Then WriteLine(stream," color: "+Self.color+";") If Self.font.length Then WriteLine(stream," font: "+Self.font+";") If Self.font_style.length Then WriteLine(stream," font-style: "+Self.font_style+";") If Self.font_weight.length Then WriteLine(stream," font-weight: "+Self.font_weight+";") If Self.text_decoration.length Then WriteLine(stream," text-decoration: "+Self.text_decoration+";") If Self.width.length Then WriteLine(stream," width: "+Self.width+";") If Self.max_width.length Then WriteLine(stream," max-width: "+Self.max_width+";") If Self.min_width.length Then WriteLine(stream," min-width: "+Self.min_width+";") If Self.height.length Then WriteLine(stream," height: "+Self.height+";") If Self.max_height.length Then WriteLine(stream," max-height: "+Self.max_height+";") If Self.min_height.length Then WriteLine(stream," min-height: "+Self.min_height+";") If Self.background.length Then WriteLine(stream," background: "+Self.background+";") If Self.border.length Then WriteLine(stream," border: "+Self.border+";") If Self.border_bottom.length Then WriteLine(stream," border-bottom: "+Self.border_bottom+";") If Self.border_left.length Then WriteLine(stream," border-left: "+Self.border_left+";") If Self.border_right.length Then WriteLine(stream," border-right: "+Self.border_right+";") If Self.border_top.length Then WriteLine(stream," border-top: "+Self.border_top+";") If Self.padding.length Then WriteLine(stream," padding: "+Self.padding+";") If Self.padding_bottom.length Then WriteLine(stream," padding-bottom: "+Self.padding_bottom+";") If Self.padding_left.length Then WriteLine(stream," padding-left: "+Self.padding_left+";") If Self.padding_right.length Then WriteLine(stream," padding-right: "+Self.padding_right+";") If Self.padding_top.length Then WriteLine(stream," padding-top: "+Self.padding_top+";") If Self.cell_spacing.length Then WriteLine(stream," cell-spacing: "+Self.cell_spacing+";") If Self.v_align.length Then WriteLine(stream," vertical-align: "+Self.v_align+";") If Self.text_align.length Then WriteLine(stream," text-align: "+Self.text_align+";") WriteLine(stream,"}") WriteLine(stream,"") End Method End Type ' ' Convert normal docs :) ConvertDocs() ' ' Write the Module docs :) WriteModDocs() Print "Finished!"
It spits out html files, looking something like this:

Have Fun!