Esempio n. 1
0
        /// <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));
        }
Esempio n. 2
0
        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;
            }
        }
Esempio n. 4
0
        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));
        }
Esempio n. 5
0
        //[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);
        }
Esempio n. 6
0
        /// <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));
            }
        }
Esempio n. 7
0
        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;
            }
        }
Esempio n. 8
0
        /// <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");
        }