/// <summary> /// Called after the view has been initialized. /// </summary> public override void AfterInitialize() { base.AfterInitialize(); // initialize designer views DesignerViews = new DesignerViewData(); // load designer view data from the object model var contentObjectModel = ContentObjectModel.GetInstance(); var designerViews = new List <DesignerView>(); foreach (var viewObject in contentObjectModel.ViewObjects.Where(x => !x.HideInDesigner)) { // ignore views that aren't scene object views if (!IsUIView(viewObject)) { continue; } designerViews.Add(new DesignerView { Id = viewObject.Name, Name = viewObject.Name, ViewTypeName = viewObject.TypeName, ViewObject = viewObject }); } DesignerViews.AddRange(designerViews.OrderBy(x => x.Id)); }
private static void CreateSceneXml() { var contentObjectModel = ContentObjectModel.GetInstance(); var path = ContentParser.GetContentFolderPathFromSelectedFile(ContentParser.ScenesFolder); var filename = ""; int i = 0; do { var sceneName = String.Format("NewScene{0}", i == 0 ? string.Empty : i.ToString()); filename = String.Format("{0}{1}.xml", path, sceneName); ++i; // find unique name if (!File.Exists(filename)) { // check if view name exists if (!contentObjectModel.SceneObjects.Any(x => x.Name == sceneName)) { // name is valid break; } } } while (true); ProjectWindowUtil.CreateAssetWithContent(filename, string.Empty); }
private static void OnPlayModeStateChanged(PlayModeStateChange state) { switch (state) { case PlayModeStateChange.EnteredEditMode: // call any queued code processing methods if (!QueuedAssetsToBeProcessed) { break; } QueuedAssetsToBeProcessed = false; var importedAssets = PostprocessBatch.ImportedAssets.ToArray(); var deletedAssets = PostprocessBatch.DeletedAssets.ToArray(); var movedAssets = PostprocessBatch.MovedAssets.ToArray(); var movedFromAssets = PostprocessBatch.MovedFromAssets.ToArray(); PostprocessBatch.Clear(); ContentAssetProcessor.OnPostprocessAllAssets(importedAssets, deletedAssets, movedAssets, movedFromAssets); break; case PlayModeStateChange.ExitingEditMode: // generate bundles if necessary var config = MasterConfig.GetInstance(); var needBuild = config.AssetsNeedBuild; if (!needBuild) { break; } var bundles = ContentObjectModel.GetInstance().AssetBundleObjects.Where(x => x.NeedBuild).ToList(); if (bundles.Count > 0) { try { ContentAssetProcessor.IgnoreChanges = true; ContentParser.BuildAssetBundles(bundles); } finally { ContentAssetProcessor.IgnoreChanges = false; } } break; case PlayModeStateChange.EnteredPlayMode: break; case PlayModeStateChange.ExitingPlayMode: break; } }
public static void GenerateApiDocTemplate() { var sb = new StringBuilder(); var apiDocsTemplateFile = "Assets/DevTools/Docs/ApiDocs-Template.txt"; var contentObjectModel = ContentObjectModel.GetInstance(); var config = MasterConfig.GetInstance(); // iterate through all views and their dependency properties foreach (var viewObject in contentObjectModel.ViewObjects.OrderBy(x => x.Name)) { var docObject = config.DocObjects.FirstOrDefault(x => x.Name == viewObject.Name); var propertyDeclarations = CodeGenerator.GetPropertyDeclarations(viewObject, false, true, true).Where(x => x.Declaration.DeclarationType != PropertyDeclarationType.Template && x.Declaration.DeclarationType != PropertyDeclarationType.View && x.Declaration.DeclarationType != PropertyDeclarationType.UnityComponent).OrderBy(x => x.Declaration.PropertyName); string docObjectComment = docObject?.Comment ?? string.Empty; sb.AppendLine("{0}: {1}", viewObject.Name, docObjectComment); if (!propertyDeclarations.Any()) { continue; } foreach (var propertyDeclaration in propertyDeclarations) { if (propertyDeclaration.Declaration.DeclarationType == PropertyDeclarationType.Template || propertyDeclaration.Declaration.DeclarationType == PropertyDeclarationType.UnityComponent || (propertyDeclaration.Declaration.DeclarationType == PropertyDeclarationType.View && !propertyDeclaration.IsMapped)) { continue; // ignore component, template and view properties as we'll generate default docs for those } var docProperty = docObject?.Properties.FirstOrDefault(x => x.Name == propertyDeclaration.Declaration.PropertyName); if (!String.IsNullOrEmpty(docProperty?.Comment)) { continue; // ignore properties that already have comments } // add empty entries for properties that aren't commented string comment = string.Empty; sb.AppendLine("- {0}: {1}", propertyDeclaration.Declaration.PropertyName, comment); } sb.AppendLine(); } // write text to file File.WriteAllText(apiDocsTemplateFile, sb.ToString()); Debug.Log(String.Format("#Delight# Documentation generated at \"{0}\"", apiDocsTemplateFile)); }
//[MenuItem("Assets/Create/Delight View + Code-behind", false, -19)] //private static void CreateViewXmlAndCodeBehind() //{ // CreateViewXml(true); //} private static void CreateViewXml(bool generateCodeBehind) { var contentObjectModel = ContentObjectModel.GetInstance(); var path = ContentParser.GetContentFolderPathFromSelectedFile(ContentParser.ViewsFolder); var filename = ""; int i = 0; string viewName = ""; do { viewName = String.Format("NewView{0}", i == 0 ? string.Empty : i.ToString()); filename = String.Format("{0}{1}.xml", path, viewName); ++i; // find unique name if (!File.Exists(filename)) { // check if view name exists if (!contentObjectModel.ViewObjects.Any(x => x.Name == viewName)) { // name is valid break; } } } while (true); // set indicator to generate code-behind once file is renamed if (generateCodeBehind) { var config = MasterConfig.GetInstance(); config.GenerateBlankCodeBehind = true; config.SaveConfig(); } // create XML file that the user will be allowed to name, once created the XML parser will generate the content ProjectWindowUtil.CreateAssetWithContent(filename, string.Empty); }
/// <summary> /// Processes XML assets that are added/changed and generates code-behind. /// </summary> public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssets) { if (EditorPrefs.GetBool("Delight_DisableAutoContentParser") && !ForceProcessing) { return; } // check if any views have been added or changed var config = MasterConfig.GetInstance(); config.UpdateExtensionContentFolders(); // update extension content folders in case new extension content is added // xml content var addedOrUpdatedXmlAssets = new List <string>(); // assets var addedOrUpdatedAssetObjects = new List <string>(); var deletedAssetObjects = new List <string>(); var movedAssetObjects = new List <string>(); var movedFromAssetObjects = new List <string>(); // data schemas var addedOrUpdatedSchemaObjects = new List <string>(); var deletedSchemaObjects = new List <string>(); var movedSchemaObjects = new List <string>(); var movedFromSchemaObjects = new List <string>(); bool rebuildConfig = false; bool rebuildViews = false; // process imported assets foreach (var path in importedAssets) { // is the asset in a content folder? var contentFolder = config.ContentFolders.FirstOrDefault(x => path.IIndexOf(x) >= 0); if (contentFolder == null) { continue; // no. } // is it an cs file? if (path.IIndexOf(".cs") >= 0) { continue; // yes. ignore } // is the asset in the assets folder? if (IsInAssetsFolder(path, contentFolder)) { addedOrUpdatedAssetObjects.Add(path); continue; } // is the asset in the models folder? if (IsInContentTypeFolder(path, contentFolder, ContentParser.ModelsFolder)) { if (!path.IContains("Schema")) { continue; } addedOrUpdatedSchemaObjects.Add(path); continue; } // is the asset a config file? if (path.IContains(".txt") && path.IContains("Config")) { // rebuild config rebuildConfig = true; continue; } // is the asset in a styles folder? if (IsInStylesFolder(path, contentFolder)) { // yes. rebuild all views rebuildViews = true; continue; } // is it an xml asset? if (path.IIndexOf(".xml") >= 0) { addedOrUpdatedXmlAssets.Add(path); } } // process deleted assets foreach (var path in deletedAssets) { // is the asset in a content folder? var contentFolder = config.ContentFolders.FirstOrDefault(x => path.IIndexOf(x) >= 0); if (contentFolder == null) { continue; // no. } // is it an cs file? if (path.IIndexOf(".cs") >= 0) { continue; // yes. ignore } // is the asset in the assets folder? if (IsInAssetsFolder(path, contentFolder)) { deletedAssetObjects.Add(path); continue; } // is the asset in the models folder? if (IsInContentTypeFolder(path, contentFolder, ContentParser.ModelsFolder)) { if (!path.IContains("Schema")) { continue; } deletedSchemaObjects.Add(path); continue; } // is the asset a config file? if (path.IContains(".txt") && path.IContains("Config")) { // rebuild config rebuildConfig = true; continue; } // is it an xml asset? if (path.IIndexOf(".xml") >= 0) { rebuildViews = true; } } // process moved assets for (int i = 0; i < movedAssets.Length; ++i) { var movedToPath = movedAssets[i]; var movedFromPath = movedFromAssets[i]; // is it an cs file? if (movedToPath.IIndexOf(".cs") >= 0) { continue; // yes. ignore } // is the asset moved to or from a content folder? var toContentFolder = config.ContentFolders.FirstOrDefault(x => movedToPath.IIndexOf(x) >= 0); var fromContentFolder = config.ContentFolders.FirstOrDefault(x => movedFromPath.IIndexOf(x) >= 0); if (toContentFolder == null && fromContentFolder == null) { continue; // no. } // is the asset in the assets folder? bool movedFromAssetFolder = IsInAssetsFolder(movedFromPath, fromContentFolder); if (IsInAssetsFolder(movedToPath, toContentFolder) || movedFromAssetFolder) { movedAssetObjects.Add(movedToPath); if (movedFromAssetFolder) { movedFromAssetObjects.Add(movedFromPath); } continue; } // is the asset in the models folder? if (IsInContentTypeFolder(movedToPath, toContentFolder, ContentParser.ModelsFolder)) { if (!movedToPath.IContains("Schema")) { continue; } movedSchemaObjects.Add(movedToPath); if (IsInContentTypeFolder(movedFromPath, fromContentFolder, ContentParser.ModelsFolder)) { movedFromSchemaObjects.Add(movedFromPath); } continue; } // is the asset a config file? if (movedToPath.IContains(".txt") && movedToPath.IContains("Config")) { // rebuild config rebuildConfig = true; continue; } // is it an xml asset? if (movedToPath.IIndexOf(".xml") >= 0) { rebuildViews = true; } } bool viewsChanged = addedOrUpdatedXmlAssets.Count() > 0; bool assetsChanged = addedOrUpdatedAssetObjects.Count() > 0 || deletedAssetObjects.Count() > 0 || movedAssetObjects.Count() > 0; bool schemasChanged = addedOrUpdatedSchemaObjects.Count() > 0 || deletedSchemaObjects.Count() > 0 || movedSchemaObjects.Count() > 0; bool contentChanged = assetsChanged || rebuildViews || viewsChanged || schemasChanged || rebuildConfig; bool logComplete = contentChanged; bool refreshScripts = false; bool generateXsdSchema = false; // if editor is playing queue assets to be processed after exiting play mode if (Application.isPlaying && !ForceProcessing) { if (contentChanged) { Debug.Log("#Delight# Content (View, Assets, etc) changed while editor is playing. Content will be processed after editor exits play mode."); EditorEventListener.AddPostProcessBatch(importedAssets, deletedAssets, movedAssets, movedFromAssets); } return; } var sw = System.Diagnostics.Stopwatch.StartNew(); // TODO for tracking processing time // check if model file is new, then rebuild all content var contentObjectModel = ContentObjectModel.GetInstance(); if (contentObjectModel.NeedRebuild) { ContentParser.RebuildAll(true, true, true, true); return; } // any config changed? if (rebuildConfig) { var result = ContentParser.ParseAllConfigFiles(); if (result.HasFlag(ConfigParseResult.RebuildAll)) { ContentParser.RebuildAll(true, true, false, false); return; } else { if (result.HasFlag(ConfigParseResult.RebuildSchemas)) { ContentParser.ParseAllSchemaFiles(); schemasChanged = false; } if (result.HasFlag(ConfigParseResult.RebuildBundles)) { ContentParser.RebuildAssets(true); assetsChanged = false; generateXsdSchema = true; } } refreshScripts = true; } // any asset objects added, moved or deleted? if (assetsChanged) { ContentParser.ParseAssetFiles(addedOrUpdatedAssetObjects, deletedAssetObjects, movedAssetObjects, movedFromAssetObjects); refreshScripts = true; generateXsdSchema = true; } // any schema files added, moved or deleted? if (schemasChanged) { ContentParser.ParseSchemaFiles(addedOrUpdatedSchemaObjects, deletedSchemaObjects, movedSchemaObjects, movedFromSchemaObjects); refreshScripts = true; generateXsdSchema = true; } // any xml content moved or deleted? if (rebuildViews) { // yes. rebuild all views ContentParser.RebuildViews(); refreshScripts = false; generateXsdSchema = false; } else if (viewsChanged) { // parse new content and generate code ContentParser.ParseXmlFiles(addedOrUpdatedXmlAssets); refreshScripts = true; generateXsdSchema = true; } if (refreshScripts) { // refresh generated scripts AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } if (generateXsdSchema) { CodeGenerator.GenerateXsdSchema(); } if (contentChanged) { ConsoleLogger.Log(String.Format("#Delight# Content processed. {0}", DateTime.Now)); //Debug.Log(String.Format("Total content processing time: {0}", sw.ElapsedMilliseconds)); } }
private static void OnPlayModeStateChanged(PlayModeStateChange state) { switch (state) { case PlayModeStateChange.EnteredEditMode: break; case PlayModeStateChange.ExitingEditMode: // generate bundles if necessary var config = MasterConfig.GetInstance(); var needBuild = config.AssetsNeedBuild; if (!needBuild) { break; } var bundles = ContentObjectModel.GetInstance().AssetBundleObjects.Where(x => x.NeedBuild).ToList(); if (bundles.Count > 0) { ContentParser.BuildAssetBundles(bundles); } break; case PlayModeStateChange.EnteredPlayMode: break; case PlayModeStateChange.ExitingPlayMode: bool exitInterrupted = false; #if DELIGHT_MODULE_TEXTMESHPRO if (IsInDelightDesigner) { //var activeScene = EditorSceneManager.GetActiveScene(); // TODO cleanup // check if delight designer has unsaved progress var viewPresenter = GameObject.Find("/Delight")?.GetComponent <ViewPresenter>(); if (viewPresenter == null) { break; } var delightDesignerView = viewPresenter.PresentedView as DelightDesigner; if (delightDesignerView == null) { break; } if (delightDesignerView.CheckForUnsavedChanges()) { EditorApplication.isPlaying = true; exitInterrupted = true; } } #endif if (!exitInterrupted && QueuedAssetsToBeProcessed) { // call any queued code processing methods var importedAssets = PostprocessBatch.ImportedAssets.ToArray(); var deletedAssets = PostprocessBatch.DeletedAssets.ToArray(); var movedAssets = PostprocessBatch.MovedAssets.ToArray(); var movedFromAssets = PostprocessBatch.MovedFromAssets.ToArray(); PostprocessBatch.Clear(); QueuedAssetsToBeProcessed = false; ContentAssetProcessor.ForceProcessing = true; ContentAssetProcessor.OnPostprocessAllAssets(importedAssets, deletedAssets, movedAssets, movedFromAssets); ContentAssetProcessor.ForceProcessing = false; } break; } }
/// <summary> /// Generates API .md docs from documentation XML, for the delight-dev.github.io website. /// </summary> public static void GenerateDocumentation() { // Creates API documentation from documentation XML. // To generate the documenation XML add the following to the Assembly-CSharp.csproj file in first PropertyGroup: // <DocumentationFile>Assets\DevTools\Docs\Documentation.XML</DocumentationFile> // And build the solution to generate the documentation XML. // Then the API docs can be built through the Delight menu item. // parse Assets\DevTools\Docs\Documentation.XML var docFile = "Assets/DevTools/Docs/Documentation.XML"; var basePath = @"C:\Projects\GitProjects\delight-dev.github.io"; var outputPath = basePath + @"\Api"; var examplesPath = basePath + @"\_includes\Examples"; if (!File.Exists(docFile)) { Debug.Log(String.Format("#Delight# Unable to generate API documentation. Couldn't find XML docs at \"{0}\". To generate XML docs add the following to the Assembly-CSharp.csproj file in first PropertyGroup:{1}{1}<DocumentationFile>Assets\\DevTools\\Docs\\Documentation.XML</DocumentationFile>{1}{1}And build the solution to generate the documentation XML, then the API docs can be generated.", docFile, Environment.NewLine)); return; } var documentationXml = File.ReadAllText("Assets/DevTools/Docs/Documentation.XML"); XElement xmlElement = null; try { xmlElement = XElement.Parse(documentationXml); } catch (Exception e) { Debug.Log(String.Format("#Delight# Unable to generate API documentation. Couldn't parse the XML docs at \"{0}\". Exception thrown: " + e.ToString())); return; } var docData = new List <DocData>(); var contentObjectModel = ContentObjectModel.GetInstance(); // parse XML comments and create document data objects foreach (var element in xmlElement.Descendants("member").Where(x => x.Attribute("name").Value.StartsWith("T:"))) { var data = new DocData(); data.FullTypeName = element.Attribute("name").Value.Substring(2); // ignore examples, editor, textmeshpro and dev-tools //if (data.FullTypeName.StartsWith("MarkLight.Examples") || data.FullTypeName.StartsWith("MarkLight.DevTools") || // data.FullTypeName.StartsWith("MarkLight.Editor") || data.FullTypeName.StartsWith("MarkLight.Views.UI.DemoMessage") || // data.FullTypeName.StartsWith("TMPro")) //{ // continue; //} data.TypeName = data.FullTypeName.Substring(data.FullTypeName.LastIndexOf(".") + 1); data.HtmlTypeName = data.TypeName.Replace("`1", "<T>"); data.View = contentObjectModel.ViewObjects?.FirstOrDefault(x => x.TypeName == data.TypeName); data.IsType = true; data.FileName = String.Format("{0}", data.TypeName.Replace("`1", "T")); // add summary data.Summary = element.Element("summary").Value.Trim(); // add description var description = element.Element("d"); if (description != null) { data.Description = description.Value.Trim(); } // find all fields associated with this type var fields = xmlElement.Descendants("member").Where(x => x.Attribute("name").Value.StartsWith(String.Format("F:{0}.", data.FullTypeName)) || x.Attribute("name").Value.StartsWith(String.Format("P:{0}.", data.FullTypeName))); foreach (var field in fields) { string fieldName = string.Empty; try { // get field summaries and descriptions fieldName = field.Attribute("name").Value.Substring(2 + data.FullTypeName.Length + 1); if (fieldName.Count(x => x == '.') > 0) { continue; } data.FieldSummaries.Add(fieldName, field.Element("summary").Value.Trim()); var fieldDescription = field.Element("d"); if (fieldDescription != null) { data.FieldDescriptions.Add(fieldName, fieldDescription.Value.Trim()); } var fieldActionData = field.Element("actionData"); if (fieldActionData != null) { data.FieldActionData.Add(fieldName, fieldActionData.Value.Trim()); } // get mapped view field summaries and descriptions foreach (var mappedSummary in field.Elements("maps")) { var mapField = mappedSummary.Attribute("field").Value; data.FieldSummaries.Add(mapField, mappedSummary.Value.Trim()); data.MappedFields.Add(mapField); } foreach (var mappedDescription in field.Elements("mapd")) { var mapField = mappedDescription.Attribute("field").Value; data.FieldDescriptions.Add(mapField, mappedDescription.Value.Trim()); } } catch (Exception e) { Debug.LogError(String.Format("#Delight# Error generating documentation for {0}, when processing field {1}. {2}", data.HtmlTypeName, fieldName, e.ToString())); } } docData.Add(data); //Debug.LogFormat("{0}: {1}", data.FileName, data.HtmlTypeName); } System.IO.Directory.CreateDirectory(outputPath); // sort by name docData = docData.OrderBy(x => x.TypeName).ToList(); // generate TOC for views and types var viewDocs = docData.Where(x => x.IsView); var typesDocs = docData.Where(x => !x.IsView); // generate view content documentation int viewCount = 0; foreach (var viewDoc in viewDocs) { var sb = new StringBuilder(); // section: header sb.AppendLine("---"); sb.AppendLine("title: {0}", viewDoc.View.Name); sb.AppendLine("parent: Views"); sb.AppendLine("grand_parent: API"); sb.AppendLine("nav_order: {0}", viewCount); sb.AppendLine("---"); // section: title sb.AppendLine(); sb.AppendLine("# {0}", viewDoc.View.Name); // section: based on if (!String.IsNullOrEmpty(viewDoc.View.BasedOn?.Name)) { sb.AppendLine(); sb.AppendLine("Based on [{0}]({0})", viewDoc.View.BasedOn?.Name); } // section: description if (!String.IsNullOrEmpty(viewDoc.Summary)) { sb.AppendLine(); sb.AppendLine("## Description"); sb.AppendLine(); sb.AppendLine("{0}", !String.IsNullOrEmpty(viewDoc.Description) ? viewDoc.Description : viewDoc.Summary); } // section: examples // check if example .md file exist for view if (File.Exists(String.Format("{0}/Views/{1}Examples.md", examplesPath, viewDoc.FileName))) { // create markdown file include sb.AppendLine(); sb.AppendLine("{{% capture {0}Examples %}}{{% include Examples/Views/{0}Examples.md %}}{{% endcapture %}}", viewDoc.FileName); sb.AppendLine("{{{{ {0}Examples | markdownify }}}}", viewDoc.FileName); } // section: dependency properties var propertyDeclarations = CodeGenerator.GetPropertyDeclarations(viewDoc.View, true, true, true); if (propertyDeclarations.Count > 0) { var dependencyProperties = propertyDeclarations.Where(x => x.Declaration.DeclarationType == PropertyDeclarationType.Default).OrderBy(x => x.Declaration.PropertyName).ToList(); if (dependencyProperties.Count > 0) { sb.AppendLine(); sb.AppendLine("## Dependency Properties"); sb.AppendLine(); sb.AppendLine("| Name | Type | Description |"); sb.AppendLine("| --- | --- | --- |"); foreach (var dependencyProperty in dependencyProperties) { var propertyName = dependencyProperty.Declaration.PropertyName; var propertyTypeName = dependencyProperty.Declaration.PropertyTypeName; int indexOfDot = propertyTypeName.LastIndexOf("."); if (indexOfDot > 0) { propertyTypeName = propertyTypeName.Substring(indexOfDot + 1); } var propertyTypeFullName = dependencyProperty.Declaration.PropertyTypeFullName; var propertyDescription = viewDoc.GetFieldSummary(propertyName); var propertyTypeDoc = docData.FirstOrDefault(x => x.TypeName == propertyTypeName); // get type name if (propertyTypeDoc != null) { propertyTypeName = String.Format("[{0}]({1}{2})", propertyTypeName, propertyTypeDoc.IsView ? "" : "../Types/", propertyTypeDoc.FileName); } else { // check if it's a unity type // check if the type is a Unity type if (propertyTypeFullName.StartsWith("UnityEngine")) { // reference the unity docs propertyTypeName = String.Format("[{0}](http://docs.unity3d.com/ScriptReference/{0}.html)", propertyTypeName); } else { // replace Boolean, String, Single, Object, etc. if (propertyTypeName == "Boolean") { propertyTypeName = "bool"; } else if (propertyTypeName == "Int32") { propertyTypeName = "int"; } else if (propertyTypeName == "Single") { propertyTypeName = "float"; } else if (propertyTypeName == "String") { propertyTypeName = "string"; } else if (propertyTypeName == "Object") { propertyTypeName = "object"; } } } // get summary if (String.IsNullOrEmpty(propertyDescription)) { // check if summary exist in any of the base types var basedOn = viewDoc.View.BasedOn; while (basedOn != null) { var basedOnViewDoc = docData.FirstOrDefault(x => x.TypeName == basedOn.TypeName); if (basedOnViewDoc != null) { var basedOnSummary = basedOnViewDoc.GetFieldSummary(propertyName); if (!String.IsNullOrEmpty(basedOnSummary)) { propertyDescription = basedOnSummary; break; } } basedOn = basedOn.BasedOn; } } // print row PropertyName | PropertyType | Description sb.AppendLine("| {0} | {1} | {2} |", propertyName, propertyTypeName, propertyDescription); } } } viewDoc.DocContent = sb.ToString(); ++viewCount; } // generate type content documentation int typeCount = 0; foreach (var typeDoc in typesDocs) { var sb = new StringBuilder(); // section: header sb.AppendLine("---"); sb.AppendLine("title: {0}", typeDoc.HtmlTypeName); sb.AppendLine("parent: Types"); sb.AppendLine("grand_parent: API"); sb.AppendLine("nav_order: {0}", typeCount); sb.AppendLine("---"); // section: title sb.AppendLine(); sb.AppendLine("# {0}", typeDoc.HtmlTypeName); // section: description if (!String.IsNullOrEmpty(typeDoc.Summary)) { sb.AppendLine(); sb.AppendLine("## Description"); sb.AppendLine(); sb.AppendLine("{0}", !String.IsNullOrEmpty(typeDoc.Description) ? typeDoc.Description : typeDoc.Summary); } typeDoc.DocContent = sb.ToString(); ++typeCount; } // generate .md files foreach (var doc in docData) { // write text to file File.WriteAllText(String.Format("{0}/{1}/{2}.md", outputPath, doc.IsView ? "Views" : "Types", doc.FileName), doc.DocContent); } Debug.Log("#Delight# Documentation generated"); }