PRPLCompiler.cs
using System;
using UnityEngine;
using System.Collections.Generic;
 
public class PRPLDocCompiler {
 
    private Main main;
    private int cmdCount;
    private string currentSection;
    private string[] docIndexLines;
    private OrderedDictionaryG<string, List<Tuple<int, string>>> sectionDict;
 
    public string resultFile;
 
    public void MakePRPLDocs(Main main, string srcDir, string outputDir) {
        this.main = main;
        resultFile = null;
        cmdCount = 0;
        currentSection = null;
        sectionDict = new OrderedDictionaryG<string, List<Tuple<int, string>>>();
        System.Text.StringBuilder sbcommands = new System.Text.StringBuilder();
		System.Text.StringBuilder sbIndex = new System.Text.StringBuilder();
 
        srcDir = srcDir.Trim();
        outputDir = outputDir.Trim();
 
        if (srcDir != "" && !srcDir.EndsWith("/") && !srcDir.EndsWith("\\")) {
            srcDir = srcDir + "/";
        }
        if (outputDir != "" && !outputDir.EndsWith("/") && !outputDir.EndsWith("\\")) {
            outputDir = outputDir + "/";
        }
 
        try {
            docIndexLines = System.IO.File.ReadAllLines(srcDir + "prpldocindex.txt");
            foreach (string i in docIndexLines) {
                ProcessDocFile(srcDir, i.Trim(), sbcommands);
            }
 
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append(@"
<html>
<head>
<style type='text/css'>
 
    html {font-family: Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace;}
	a {text-decoration: none;}
	a:link {color: green;}
	a:visited {color: green;}
	a:hover {color: blue;}
	a:active {color: black;}	
    code {
        font-size: 90%;
        line-height: 1.2em;
        font-family: Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace;
        white-space: pre;
        white-space: pre-wrap;
        white-space: -moz-pre-wrap;
        white-space: -o-pre-wrap;
        display: block;
        color: #555555;
		background: #f4f4f4;
	}
	.command_syntax { padding: 1px 5px; font-size: 110%; font-weight:bold; background-color: #bfc7cf; color: #000000; width:850px; }
	.command_body{ padding: 1px 1px 50px 15px; }
	.example{ padding: 0px 0px 5px 0px; }
	.description{ padding: 0px 0px 5px 0px; }
	.index {border-spacing:20px;}
	.index tr{vertical-align:top;}
	.section {width: 400px; background-color:#d0d0d0; border-spacing:1px;}
    .section th{ font-size: 120%;}
	.section td{ padding: 0px 0px 0px 4px;}
	.section tr:nth-child(odd) {background-color: #f7f7f7;}
	.section tr:nth-child(even) {background-color: #ffffff;}
	.sectionHead{background-color:#e0e7ef !important;}
	.fullList{font-size:55%; padding:0}
</style >
</head >
<body>
<div id=""wrapper"" style=""display:flex"">
");
			sb.Append("");
			GenerateDocSections(sb,sbIndex);
			sb.Append("\n<div style=\"float:right\">");
            sb.Append(sbcommands);
			sb.Append(@"	</div>
</div>
<div class=""fullList""><p>
");
			sb.Append(sbIndex);
			sb.Append(@"</p></div>
<script type=""text/javascript"">
function handleListClick(e) {
	var anchor = e.target;
	var cmd = anchor.firstChild.nodeValue;
 
	//funcById(""commandList"",close);
	//reset();
	//funcById(""topLink"",open);
	funcById(cmd,toggle);
	document.title = defaultTitle + "": "" + cmd;
}
 
function handleListSectionTitleClick(e) {
	toggle(e.target.parentElement.parentElement.nextElementSibling);
	document.title = defaultTitle;
}
 
function toggle(elem) {
	if (elem.style.display == ""none"") {
		elem.style.display = """";
	} else {
		elem.style.display = ""none"";
	}
}
 
function funcById(id, func) {
	func(document.getElementById(id));
}
 
function close(elem) {
	elem.style.display = ""none"";
}
 
function open(elem) {
	elem.style.display = """";
}
 
var defaultTitle = ""PRPL Docs"";
document.title = defaultTitle;
 
var wrapper = document.body.firstElementChild;
var cmdList = wrapper.firstElementChild;
var cmdSection = cmdList.firstElementChild;
while (cmdSection !== null) {
	close(cmdSection.firstElementChild.nextElementSibling);
	cmdSection = cmdSection.nextElementSibling;
}
//var toplink = cmdList.nextElementSibling;
 
function reset() {
	var docSection = cmdList.nextElementSibling;//start at the first doc section.
	var docCmd;
	//close(toplink);
	open(cmdList);
	while (docSection.tagName !== ""SCRIPT"") {
		docCmd = docSection.firstElementChild;
		while (docCmd !== null) {
			close(docCmd);
			docCmd = docCmd.nextElementSibling;
		}
		docSection = docSection.nextElementSibling;
	}
}
reset();
</script>
</body>
</html>");
            System.IO.File.WriteAllText(outputDir + "PRPLDocs.html", sb.ToString());
            resultFile = outputDir + "PRPLDocs.html";
            main.LogMessage("COMPLETE: HTML file created: " + resultFile);
        } catch (Exception e) {
            main.LogMessage("ERROR: " + e.Message);
            main.LogMessage(e.StackTrace);
        }
    }
 
    private void ProcessDocFile(string srcDir, string sect, System.Text.StringBuilder sbcommands) {
        currentSection = sect;
        string f = srcDir + sect + ".txt";
        main.LogMessage("Processing File: " + f);
        if (System.IO.File.Exists(f)) {
            string[] lines = System.IO.File.ReadAllLines(f);
            for (int i = 0; i < lines.Length; i++) {
                string l = lines[i].TrimStart();
                if (l.StartsWith("=CMD")) {
                    i = ProcessDocCMD(sbcommands, lines, i);
                }
            }
        } else {
            main.LogMessage("File Not Found: " + f);
        }
    }
 
	private void GenerateDocSections(System.Text.StringBuilder sb, System.Text.StringBuilder sbIndex) {
		sb.Append("<div id=\"commandList\" style=\"float:left\">\r\n");
 
        //bool open = false;
        for (int i = 0; i < docIndexLines.Length; i++) {
 
			//sb.Append("<td>\r\n");
            if (!sectionDict.ContainsKey(docIndexLines[i].Trim())) {
                sb.Append("&nbsp;");
            } else {
				GenerateDocSection(sb, sbIndex, docIndexLines[i].Trim());
            }
            //sb.Append("</td>\r\n");
 
            //if (i % 2 == 1) {
               // sb.Append("</tr>\r\n");
                //open = false;
            //}
        }
        //if (open) {
            //sb.Append("</tr>\r\n");
            //open = false;
        //}
        /*
        sb.Append("<tr>\r\n");
        sb.Append("<td>\r\n");
        GenerateDocSection(sb, "Vars and Functions");
        sb.Append("</td>\r\n");
 
        sb.Append("<td>\r\n");
        GenerateDocSection(sb, "Logic");
        sb.Append("</td>\r\n");
 
        sb.Append("</tr>\r\n");
        */
 
		sb.Append("</div>");
 
    }
 
	private void GenerateDocSection(System.Text.StringBuilder sb, System.Text.StringBuilder sbIndex, string section) {
        int cols = 1;
		sb.Append("<table class='section'>\n\t<thead>\n\t\t<tr>\n\t\t\t<th colspan='" + cols.ToString() +"' onclick=handleListSectionTitleClick(event)>" + section + "</th>\r\n");
		sb.Append("\t\t</tr>\n\t</thead>\n\t<tbody>\n");
        List<Tuple<int, string>> list = sectionDict[section];
        int cc = 0;
		//sbIndex.Append("<p>\n");
        // open = false;
        foreach (Tuple<int, string> tup in list) {
            if (cc % cols == 0) {
                sb.Append("\t\t<tr>\r\n");
                //open = true;
            }
 
            string cs;
            if (cc == list.Count - 1) {
                cs = "colspan='" + (cols - (cc % cols)).ToString() + "'";
            } else {
                cs = "";
            }
			sb.Append("\t\t\t<td " + cs + " >\n\t\t\t\t<a onclick=handleListClick(event)>" + tup.Item2 + "</a>\n\t\t\t</td>\r\n");
			sbIndex.Append("  <a onclick=handleListClick(event)>" + tup.Item2 + "</a>  \r\n");
 
            if (cc % cols == cols-1) {
                sb.Append("\t\t</tr>\r\n");
                //open = false;
            }
            cc++;
        }
        //if (open) {
			sb.Append("\t</tbody>\r\n");
           // open = false;
        //}
		//sbIndex.Append("</p>\n");
        sb.Append("</table>\r\n");
    }
 
 
    private int ProcessDocCMD(System.Text.StringBuilder sb, string[] lines, int pos) {
		System.Text.StringBuilder currCmdStr = new System.Text.StringBuilder ();
		string cmd = "";
 
		sb.Append (@"
	<div class='command' id=""");
		currCmdStr.Append("\" >\n");
        string startingSection = currentSection;
        int i;
        for (i = pos; i < lines.Length; i++) {
            string l = lines[i].TrimStart();
            if (l.StartsWith("=ENDCMD")) {
                break;
            } else
            if (l.StartsWith("=CMDCLASS")) {
					ProcessDocCMDCLASS(currCmdStr, l);
            } else
            if (l.StartsWith("=COMMAND")) {
					cmd = ProcessDocCOMMAND(currCmdStr, l);
            } else
            if (l.StartsWith("=DESC")) {
					i = ProcessDocDESC(currCmdStr, lines, i);
            } else
            if (l.StartsWith("=EX")) {
					i = ProcessDocEX(currCmdStr, lines, i);
            }
        }
        currentSection = startingSection;
		sb.Append(cmd);
		sb.Append(currCmdStr);
        sb.Append("\t\t</div>\r\n");
        sb.Append("\t</div>\r\n");
 
        return i;
    }
 
    private void ProcessDocCMDCLASS(System.Text.StringBuilder sb, string l) {
        string sect = l.Substring("=CMDCLASS".Length).Trim();
        if (sect != "") {
            currentSection = sect;
        }
        //sb.Append("<a name='"+sect+"'></a>\r\n");
    }
 
    private string ProcessDocCOMMAND(System.Text.StringBuilder sb, string l) {
        string cmd = l.Substring("=COMMAND".Length).Trim();
        string[] splitcmd = cmd.Split(' ', '(');
        List<Tuple<int, string>> section = null;
        if (sectionDict.ContainsKey(currentSection)) {
            section = sectionDict[currentSection];
        } else {
            section = new List<Tuple<int, string>>();
            sectionDict[currentSection] = section;
        }
        section.Add(new Tuple<int, string>(cmdCount, splitcmd[0]));
		sb.Append("\t\t<div class='command_syntax'><a onclick=handleListClick(event)>");
        sb.Append(cmd);
        //sb.Append("<span class='backtotop'><a href='#index'>[TOP]</a></span>");
        sb.Append("</a></div>\r\n");
        cmdCount++;
		return splitcmd [0];
    }
 
    private int ProcessDocDESC(System.Text.StringBuilder sb, string[] lines, int pos) {
        sb.Append("\t\t<div class='command_body'>\r\n");
        sb.Append("\t\t\t<div class='description'>\r\n");
        sb.Append("\t\t\t\t<b>Description</b>\r\n");
        sb.Append("\t\t\t\t<div>\r\n");
        int i;
        for (i = pos + 1; i < lines.Length; i++) {
            if (lines[i].Trim() == "=ENDDESC") {
                break;
            } else {
                //sb.Append(lines[i].TrimStart().Substring(2) + " ");
                sb.Append(lines[i].TrimEnd() + " ");
            }
        }
        sb.Append("\n\t\t\t</div>\r\n");
        sb.Append("\t\t</div>\r\n");
 
        sb.Append("\t\t<div>\r\n");
        sb.Append("\t\t\t<b>Examples</b>\r\n");
        sb.Append("\t\t</div>\r\n");
        return i;
    }
 
    private int ProcessDocEX(System.Text.StringBuilder sb, string[] lines, int pos) {
        sb.Append("\t\t\t<div class='example'>\r\n");
        sb.Append("\t\t\t\t<code>\n");
        int i;
        for (i = pos + 1; i < lines.Length; i++) {
            if (lines[i].Trim() == "=ENDEX") {
                break;
            } else {
                //sb.Append(lines[i].TrimStart().Substring(2) + "\r\n");
                sb.Append(lines[i].TrimEnd() + "\r\n");
            }
        }
        sb.Append("\t\t\t\t

\r\n“);

      sb.Append("\t\t\t</div>\r\n");
      return i;
  }

}

</code>