<#@ assembly name="System.Core" #> <#@ assembly name="System.Data" #> <#@ assembly name="System.Data.Entity" #> <#@ assembly name="System.Xml" #> <#@ assembly name="System.Xml.Linq"#> <#@ assembly name="System.Windows.Forms" #> <#@ assembly name="EnvDTE"#> <#@ assembly name="EnvDTE80" #> <#@ assembly name="Microsoft.VisualStudio.Shell.10.0"#> <#@ assembly name="Microsoft.VisualStudio.Shell.Interop "#> <#@ import namespace="Microsoft.VisualStudio.Shell.Interop"#> <#@ import namespace="System" #> <#@ import namespace="System.Data" #> <#@ import namespace="System.Data.Objects" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Data.Objects.DataClasses" #> <#@ import namespace="System.Text.RegularExpressions" #> <#@ import namespace="System.Xml" #> <#@ import namespace="System.Xml.Linq" #> <#@ import namespace="System.Globalization" #> <#@ import namespace="System.Reflection" #> <#@ import namespace="System.CodeDom" #> <#@ import namespace="System.CodeDom.Compiler" #> <#@ import namespace="Microsoft.CSharp"#> <#@ import namespace="System.Text"#> <#@ import namespace="EnvDTE" #> <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> <#+ /* This software is supplied "AS IS". The authors disclaim all warranties, expressed or implied, including, without limitation, the warranties of merchantability and of fitness for any purpose. The authors assume no liability for direct, indirect, incidental, special, exemplary, or consequential damages, which may result from the use of this software, even if advised of the possibility of such damage. The TemplateFileManager is based on EntityFrameworkTemplateFileManager (EFTFM) from MS. Differences to EFTFM Version 2.1: - Replace Enum BuildAction with class for more flexibility Version 2: - StartHeader works with Parameter $filename$ - StartNewFile has a new named parameter FileProperties - Support for: - BuildAction - CustomTool - user defined parameter for using in StartHeader-Block - Property IsAutoIndentEnabled for support Format Document (C#, VB) when set to true Version: 1.1 Add method WriteLineToBuildPane, WriteToBuildPane Version 1: - StartNewFile with named parameters projectName and folderName for generating files to different locations. - Property CanOverrideExistingFile, to define whether existing files are can overwritten - Property Encoding Encode type for output files. */ /// /// Writes a line to the build pane in visual studio and activates it /// /// Text to output - a \n is appended void WriteLineToBuildPane (string message){ WriteLineToBuildPane(String.Format("{0}\n", message)); } /// /// Writes a string to the build pane in visual studio and activates it /// /// Text to output void WriteToBuildPane (string message){ IVsOutputWindow outWindow = (this.Host as IServiceProvider).GetService( typeof( SVsOutputWindow ) ) as IVsOutputWindow; Guid generalPaneGuid = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid; // P.S. There's also the GUID_OutWindowDebugPane available. IVsOutputWindowPane generalPane; outWindow.GetPane( ref generalPaneGuid , out generalPane ); generalPane.OutputString( message ); generalPane.Activate(); // Brings this pane into view } /// /// Responsible for marking the various sections of the generation, /// so they can be split up into separate files and projects /// /// R. Leupold public class TemplateFileManager { private EnvDTE.ProjectItem templateProjectItem; private Action checkOutAction; private Action> projectSyncAction; private EnvDTE.DTE dte; private List templatePlaceholderList = new List(); /// /// Creates files with VS sync /// public static TemplateFileManager Create(object textTransformation) { DynamicTextTransformation2 transformation = DynamicTextTransformation2.Create(textTransformation); IDynamicHost2 host = transformation.Host; return new TemplateFileManager(transformation); } private readonly List files = new List(); private readonly Block footer = new Block(); private readonly Block header = new Block(); private readonly DynamicTextTransformation2 _textTransformation; // reference to the GenerationEnvironment StringBuilder on the // TextTransformation object private readonly StringBuilder _generationEnvironment; private Block currentBlock; /// /// Initializes an TemplateFileManager Instance with the /// TextTransformation (T4 generated class) that is currently running /// private TemplateFileManager(object textTransformation) { if (textTransformation == null) { throw new ArgumentNullException("textTransformation"); } _textTransformation = DynamicTextTransformation2.Create(textTransformation); _generationEnvironment = _textTransformation.GenerationEnvironment; var hostServiceProvider = _textTransformation.Host.AsIServiceProvider(); if (hostServiceProvider == null) { throw new ArgumentNullException("Could not obtain hostServiceProvider"); } dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) { throw new ArgumentNullException("Could not obtain DTE from host"); } this.templateProjectItem = dte.Solution.FindProjectItem(_textTransformation.Host.TemplateFile); this.CanOverrideExistingFile = true; this.IsAutoIndentEnabled = false; this.Encoding = System.Text.Encoding.UTF8; checkOutAction = fileName => dte.SourceControl.CheckOutItem(fileName); projectSyncAction = keepFileNames => ProjectSync(templateProjectItem, keepFileNames); } /// /// If set to false, existing files are not overwritten /// /// public bool CanOverrideExistingFile { get; set; } /// /// If set to true, output files (c#, vb) are formatted based on the vs settings. /// /// public bool IsAutoIndentEnabled { get; set; } /// /// Defines Encoding format for generated output file. (Default UTF8) /// /// public System.Text.Encoding Encoding { get; set; } /// /// Marks the end of the last file if there was one, and starts a new /// and marks this point in generation as a new file. /// /// Filename /// Name of the target project for the new file. /// Name of the target folder for the new file. /// File property settings in vs for the new File public void StartNewFile(string name , string projectName = "", string folderName = "", FileProperties fileProperties = null) { if (String.IsNullOrWhiteSpace(name) == true) { throw new ArgumentException("name"); } CurrentBlock = new Block { Name = name, ProjectName = projectName, FolderName = folderName, FileProperties = fileProperties ?? new FileProperties() }; } public void StartFooter() { CurrentBlock = footer; } public void StartHeader() { CurrentBlock = header; } public void EndBlock() { if (CurrentBlock == null) { return; } CurrentBlock.Length = _generationEnvironment.Length - CurrentBlock.Start; if (CurrentBlock != header && CurrentBlock != footer) { files.Add(CurrentBlock); } currentBlock = null; } /// /// Produce the template output files. /// public virtual IEnumerable Process(bool split = true) { var list = new List(); if (split) { EndBlock(); var headerText = _generationEnvironment.ToString(header.Start, header.Length); var footerText = _generationEnvironment.ToString(footer.Start, footer.Length); files.Reverse(); foreach (var block in files) { var outputPath = VSHelper.GetOutputPath(dte, block, Path.GetDirectoryName(_textTransformation.Host.TemplateFile)); var fileName = Path.Combine(outputPath, block.Name); var content = this.ReplaceParameter(headerText, block) + _generationEnvironment.ToString(block.Start, block.Length) + footerText; var file = new OutputFile { FileName = fileName, ProjectName = block.ProjectName, FolderName = block.FolderName, FileProperties = block.FileProperties, Content = content }; CreateFile(file); _generationEnvironment.Remove(block.Start, block.Length); list.Add(file); } } projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(list, null, null)); this.CleanUpTemplatePlaceholders(); var items = VSHelper.GetOutputFilesAsProjectItems(this.dte, list); this.WriteVsProperties(items, list); if (this.IsAutoIndentEnabled == true && split == true) { this.FormatProjectItems(items); } this.WriteLog(list); return list; } private void FormatProjectItems(IEnumerable items) { foreach (var item in items) { this._textTransformation.WriteLine( VSHelper.ExecuteVsCommand(this.dte, item, "Edit.FormatDocument")); //, "Edit.RemoveAndSort")); this._textTransformation.WriteLine("//-> " + item.Name); } } private void WriteVsProperties(IEnumerable items, IEnumerable outputFiles) { foreach (var file in outputFiles) { var item = items.Where(p => p.Name == Path.GetFileName(file.FileName)).FirstOrDefault(); if (item == null) continue; if (String.IsNullOrEmpty(file.FileProperties.CustomTool) == false) { VSHelper.SetPropertyValue(item, "CustomTool", file.FileProperties.CustomTool); } if (String.IsNullOrEmpty(file.FileProperties.BuildActionString) == false) { VSHelper.SetPropertyValue(item, "ItemType", file.FileProperties.BuildActionString); } } } private string ReplaceParameter(string text, Block block) { if (String.IsNullOrEmpty(text) == false) { text = text.Replace("$filename$", block.Name); } foreach (var item in block.FileProperties.TemplateParameter.AsEnumerable()) { text = text.Replace(item.Key, item.Value); } return text; } /// /// Write log to the default output file. /// /// private void WriteLog(IEnumerable list) { this._textTransformation.WriteLine("// Generated helper templates"); foreach (var item in templatePlaceholderList) { this._textTransformation.WriteLine("// " + this.GetDirectorySolutionRelative(item)); } this._textTransformation.WriteLine("// Generated items"); foreach (var item in list) { this._textTransformation.WriteLine("// " + this.GetDirectorySolutionRelative(item.FileName)); } } /// /// Removes old template placeholders from the solution. /// private void CleanUpTemplatePlaceholders() { string[] activeTemplateFullNames = this.templatePlaceholderList.ToArray(); string[] allHelperTemplateFullNames = VSHelper.GetAllSolutionItems(this.dte) .Where(p => p.Name == VSHelper.GetTemplatePlaceholderName(this.templateProjectItem)) .Select(p => VSHelper.GetProjectItemFullPath(p)) .ToArray(); var delta = allHelperTemplateFullNames.Except(activeTemplateFullNames).ToArray(); var dirtyHelperTemplates = VSHelper.GetAllSolutionItems(this.dte) .Where(p => delta.Contains(VSHelper.GetProjectItemFullPath(p))); foreach (ProjectItem item in dirtyHelperTemplates) { if (item.ProjectItems != null) { foreach (ProjectItem subItem in item.ProjectItems) { subItem.Remove(); } } item.Remove(); } } /// /// Gets a list of helper templates from the log. /// /// List of generated helper templates. private string[] GetPreviousTemplatePlaceholdersFromLog() { string path = Path.GetDirectoryName(this._textTransformation.Host.ResolvePath(this._textTransformation.Host.TemplateFile)); string file1 = Path.GetFileNameWithoutExtension(this._textTransformation.Host.TemplateFile) + ".txt"; string contentPrevious = File.ReadAllText(Path.Combine(path, file1)); var result = contentPrevious .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Split(new[] { "=>" }, StringSplitOptions.RemoveEmptyEntries).First()) .Select(x => Regex.Replace(x, "//", String.Empty).Trim()) .Where(x => x.EndsWith(VSHelper.GetTemplatePlaceholderName(this.templateProjectItem))) .ToArray(); return result; } private string GetDirectorySolutionRelative(string fullName) { int slnPos = fullName.IndexOf(Path.GetFileNameWithoutExtension(this.dte.Solution.FileName)); if (slnPos < 0) { slnPos = 0; } return fullName.Substring(slnPos); } protected virtual void CreateFile(OutputFile file) { if (this.CanOverrideExistingFile == false && File.Exists(file.FileName) == true) { return; } if (IsFileContentDifferent(file)) { CheckoutFileIfRequired(file.FileName); File.WriteAllText(file.FileName, file.Content, this.Encoding); } } protected bool IsFileContentDifferent(OutputFile file) { return !(File.Exists(file.FileName) && File.ReadAllText(file.FileName) == file.Content); } private Block CurrentBlock { get { return currentBlock; } set { if (CurrentBlock != null) { EndBlock(); } if (value != null) { value.Start = _generationEnvironment.Length; } currentBlock = value; } } private void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable keepFileNames) { var groupedFileNames = from f in keepFileNames group f by new { f.ProjectName, f.FolderName } into l select new { ProjectName = l.Key.ProjectName, FolderName = l.Key.FolderName, FirstItem = l.First(), OutputFiles = l }; this.templatePlaceholderList.Clear(); foreach (var item in groupedFileNames) { EnvDTE.ProjectItem pi = VSHelper.GetTemplateProjectItem(templateProjectItem.DTE, item.FirstItem, templateProjectItem); ProjectSyncPart(pi, item.OutputFiles); if (pi.Name.EndsWith("txt4")) this.templatePlaceholderList.Add(VSHelper.GetProjectItemFullPath(pi)); } // clean up bool hasDefaultItems = groupedFileNames.Where(f => String.IsNullOrEmpty(f.ProjectName) && String.IsNullOrEmpty(f.FolderName)).Count() > 0; if (hasDefaultItems == false) { ProjectSyncPart(templateProjectItem, new List()); } } private static void ProjectSyncPart(EnvDTE.ProjectItem templateProjectItem, IEnumerable keepFileNames) { var keepFileNameSet = new HashSet(keepFileNames); var projectFiles = new Dictionary(); var originalOutput = Path.GetFileNameWithoutExtension(templateProjectItem.FileNames[0]); foreach (EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) { projectFiles.Add(projectItem.FileNames[0], projectItem); } // Remove unused items from the project foreach (var pair in projectFiles) { bool isNotFound = keepFileNames.Where(f=>f.FileName == pair.Key).Count() == 0; if (isNotFound == true && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalOutput + ".")) { pair.Value.Delete(); } } // Add missing files to the project foreach (var fileName in keepFileNameSet) { if (!projectFiles.ContainsKey(fileName.FileName)) { templateProjectItem.ProjectItems.AddFromFile(fileName.FileName); } } } private void CheckoutFileIfRequired(string fileName) { if (dte.SourceControl == null || !dte.SourceControl.IsItemUnderSCC(fileName) || dte.SourceControl.IsItemCheckedOut(fileName)) { return; } // run on worker thread to prevent T4 calling back into VS checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null)); } } /// /// Responsible creating an instance that can be passed /// to helper classes that need to access the TextTransformation /// members. It accesses member by name and signature rather than /// by type. This is necessary when the /// template is being used in Preprocessed mode /// and there is no common known type that can be /// passed instead /// public class DynamicTextTransformation2 { private object _instance; IDynamicHost2 _dynamicHost; private readonly MethodInfo _write; private readonly MethodInfo _writeLine; private readonly PropertyInfo _generationEnvironment; private readonly PropertyInfo _errors; private readonly PropertyInfo _host; /// /// Creates an instance of the DynamicTextTransformation class around the passed in /// TextTransformation shapped instance passed in, or if the passed in instance /// already is a DynamicTextTransformation, it casts it and sends it back. /// public static DynamicTextTransformation2 Create(object instance) { if (instance == null) { throw new ArgumentNullException("instance"); } DynamicTextTransformation2 textTransformation = instance as DynamicTextTransformation2; if (textTransformation != null) { return textTransformation; } return new DynamicTextTransformation2(instance); } private DynamicTextTransformation2(object instance) { _instance = instance; Type type = _instance.GetType(); _write = type.GetMethod("Write", new Type[] { typeof(string) }); _writeLine = type.GetMethod("WriteLine", new Type[] { typeof(string) }); _generationEnvironment = type.GetProperty("GenerationEnvironment", BindingFlags.Instance | BindingFlags.NonPublic); _host = type.GetProperty("Host"); _errors = type.GetProperty("Errors"); } /// /// Gets the value of the wrapped TextTranformation instance's GenerationEnvironment property /// public StringBuilder GenerationEnvironment { get { return (StringBuilder)_generationEnvironment.GetValue(_instance, null); } } /// /// Gets the value of the wrapped TextTranformation instance's Errors property /// public System.CodeDom.Compiler.CompilerErrorCollection Errors { get { return (System.CodeDom.Compiler.CompilerErrorCollection)_errors.GetValue(_instance, null); } } /// /// Calls the wrapped TextTranformation instance's Write method. /// public void Write(string text) { _write.Invoke(_instance, new object[] { text }); } /// /// Calls the wrapped TextTranformation instance's WriteLine method. /// public void WriteLine(string text) { _writeLine.Invoke(_instance, new object[] { text }); } /// /// Gets the value of the wrapped TextTranformation instance's Host property /// if available (shows up when hostspecific is set to true in the template directive) and returns /// the appropriate implementation of IDynamicHost /// public IDynamicHost2 Host { get { if (_dynamicHost == null) { if(_host == null) { _dynamicHost = new NullHost2(); } else { _dynamicHost = new DynamicHost2(_host.GetValue(_instance, null)); } } return _dynamicHost; } } } /// /// Reponsible for abstracting the use of Host between times /// when it is available and not /// public interface IDynamicHost2 { /// /// An abstracted call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolveParameterValue /// string ResolveParameterValue(string id, string name, string otherName); /// /// An abstracted call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolvePath /// string ResolvePath(string path); /// /// An abstracted call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost TemplateFile /// string TemplateFile { get; } /// /// Returns the Host instance cast as an IServiceProvider /// IServiceProvider AsIServiceProvider(); } /// /// Reponsible for implementing the IDynamicHost as a dynamic /// shape wrapper over the Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost interface /// rather than type dependent wrapper. We don't use the /// interface type so that the code can be run in preprocessed mode /// on a .net framework only installed machine. /// public class DynamicHost2 : IDynamicHost2 { private readonly object _instance; private readonly MethodInfo _resolveParameterValue; private readonly MethodInfo _resolvePath; private readonly PropertyInfo _templateFile; /// /// Creates an instance of the DynamicHost class around the passed in /// Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost shapped instance passed in. /// public DynamicHost2(object instance) { _instance = instance; Type type = _instance.GetType(); _resolveParameterValue = type.GetMethod("ResolveParameterValue", new Type[] { typeof(string), typeof(string), typeof(string) }); _resolvePath = type.GetMethod("ResolvePath", new Type[] { typeof(string) }); _templateFile = type.GetProperty("TemplateFile"); } /// /// A call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolveParameterValue /// public string ResolveParameterValue(string id, string name, string otherName) { return (string)_resolveParameterValue.Invoke(_instance, new object[] { id, name, otherName }); } /// /// A call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolvePath /// public string ResolvePath(string path) { return (string)_resolvePath.Invoke(_instance, new object[] { path }); } /// /// A call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost TemplateFile /// public string TemplateFile { get { return (string)_templateFile.GetValue(_instance, null); } } /// /// Returns the Host instance cast as an IServiceProvider /// public IServiceProvider AsIServiceProvider() { return _instance as IServiceProvider; } } /// /// Reponsible for implementing the IDynamicHost when the /// Host property is not available on the TextTemplating type. The Host /// property only exists when the hostspecific attribute of the template /// directive is set to true. /// public class NullHost2 : IDynamicHost2 { /// /// An abstraction of the call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolveParameterValue /// that simply retuns null. /// public string ResolveParameterValue(string id, string name, string otherName) { return null; } /// /// An abstraction of the call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolvePath /// that simply retuns the path passed in. /// public string ResolvePath(string path) { return path; } /// /// An abstraction of the call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost TemplateFile /// that returns null. /// public string TemplateFile { get { return null; } } /// /// Returns null. /// public IServiceProvider AsIServiceProvider() { return null; } } public sealed class Block { public String Name; public int Start, Length; public string ProjectName { get; set; } public string FolderName { get; set; } public FileProperties FileProperties { get; set; } } public class ParamTextTemplate { private ITextTemplatingEngineHost Host { get; set; } private ParamTextTemplate(ITextTemplatingEngineHost host) { this.Host = host; } public static ParamTextTemplate Create(ITextTemplatingEngineHost host) { return new ParamTextTemplate(host); } public static TextTemplatingSession GetSessionObject() { return new TextTemplatingSession(); } public string TransformText(string templateName, TextTemplatingSession session) { return this.GetTemplateContent(templateName, session); } public string GetTemplateContent(string templateName, TextTemplatingSession session) { string fullName = this.Host.ResolvePath(templateName); string templateContent = File.ReadAllText(fullName); var sessionHost = this.Host as ITextTemplatingSessionHost; sessionHost.Session = session; Engine engine = new Engine(); return engine.ProcessTemplate(templateContent, this.Host); } } public class VSHelper { /// /// Execute Visual Studio commands against the project item. /// /// The current project item. /// The vs command as string. /// An error message if the command fails. public static string ExecuteVsCommand(EnvDTE.DTE dte, EnvDTE.ProjectItem item, params string[] command) { if (item == null) { throw new ArgumentNullException("item"); } string error = String.Empty; try { EnvDTE.Window window = item.Open(); window.Activate(); foreach (var cmd in command) { if (String.IsNullOrWhiteSpace(cmd) == true) { continue; } EnvDTE80.DTE2 dte2 = dte as EnvDTE80.DTE2; dte2.ExecuteCommand(cmd, String.Empty); } item.Save(); window.Visible = false; // window.Close(); // Ends VS, but not the tab :( } catch (Exception ex) { error = String.Format("Error processing file {0} {1}", item.Name, ex.Message); } return error; } /// /// Sets a property value for the vs project item. /// public static void SetPropertyValue(EnvDTE.ProjectItem item, string propertyName, object value) { EnvDTE.Property property = item.Properties.Item(propertyName); if (property == null) { throw new ArgumentException(String.Format("The property {0} was not found.", propertyName)); } else { property.Value = value; } } public static IEnumerable GetOutputFilesAsProjectItems(EnvDTE.DTE dte, IEnumerable outputFiles) { var fileNames = (from o in outputFiles select Path.GetFileName(o.FileName)).ToArray(); return VSHelper.GetAllSolutionItems(dte).Where(f => fileNames.Contains(f.Name)); } public static string GetOutputPath(EnvDTE.DTE dte, Block block, string defaultPath) { if (String.IsNullOrEmpty(block.ProjectName) == true && String.IsNullOrEmpty(block.FolderName) == true) { return defaultPath; } EnvDTE.Project prj = null; EnvDTE.ProjectItem item = null; if (String.IsNullOrEmpty(block.ProjectName) == false) { prj = GetProject(dte, block.ProjectName); } if (String.IsNullOrEmpty(block.FolderName) == true && prj != null) { return Path.GetDirectoryName(prj.FullName); } else if (prj != null && String.IsNullOrEmpty(block.FolderName) == false) { item = GetAllProjectItemsRecursive(prj.ProjectItems).Where(i=>i.Name == block.FolderName).First(); } else if (String.IsNullOrEmpty(block.FolderName) == false) { item = GetAllProjectItemsRecursive( dte.ActiveDocument.ProjectItem.ContainingProject.ProjectItems). Where(i=>i.Name == block.FolderName).First(); } if (item != null) { return GetProjectItemFullPath(item); } return defaultPath; } public static string GetTemplatePlaceholderName(EnvDTE.ProjectItem item) { return String.Format("{0}.txt4", Path.GetFileNameWithoutExtension(item.Name)); } public static EnvDTE.ProjectItem GetTemplateProjectItem(EnvDTE.DTE dte, OutputFile file, EnvDTE.ProjectItem defaultItem) { if (String.IsNullOrEmpty(file.ProjectName) == true && String.IsNullOrEmpty(file.FolderName) == true) { return defaultItem; } string templatePlaceholder = GetTemplatePlaceholderName(defaultItem); string itemPath = Path.GetDirectoryName(file.FileName); string fullName = Path.Combine(itemPath, templatePlaceholder); EnvDTE.Project prj = null; EnvDTE.ProjectItem item = null; if (String.IsNullOrEmpty(file.ProjectName) == false) { prj = GetProject(dte, file.ProjectName); } if (String.IsNullOrEmpty(file.FolderName) == true && prj != null) { return FindProjectItem(prj.ProjectItems, fullName, true); } else if (prj != null && String.IsNullOrEmpty(file.FolderName) == false) { item = GetAllProjectItemsRecursive(prj.ProjectItems).Where(i=>i.Name == file.FolderName).First(); } else if (String.IsNullOrEmpty(file.FolderName) == false) { item = GetAllProjectItemsRecursive( dte.ActiveDocument.ProjectItem.ContainingProject.ProjectItems). Where(i=>i.Name == file.FolderName).First(); } if (item != null) { return FindProjectItem(item.ProjectItems, fullName, true); } return defaultItem; } private static EnvDTE.ProjectItem FindProjectItem(EnvDTE.ProjectItems items, string fullName, bool canCreateIfNotExists) { EnvDTE.ProjectItem item = (from i in items.Cast() where i.Name == Path.GetFileName(fullName) select i).FirstOrDefault(); if (item == null) { File.CreateText(fullName); item = items.AddFromFile(fullName); } return item; } public static EnvDTE.Project GetProject(EnvDTE.DTE dte, string projectName) { return GetAllProjects(dte).Where(p=>p.Name == projectName).First(); } public static IEnumerable GetAllProjects(EnvDTE.DTE dte) { List projectList = new List(); var folders = dte.Solution.Projects.Cast().Where(p=>p.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder); foreach (EnvDTE.Project folder in folders) { if (folder.ProjectItems == null) continue; foreach (EnvDTE.ProjectItem item in folder.ProjectItems) { if (item.Object is EnvDTE.Project) projectList.Add(item.Object as EnvDTE.Project); } } var projects = dte.Solution.Projects.Cast().Where(p=>p.Kind != EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder); if (projects.Count() > 0) projectList.AddRange(projects); return projectList; } public static EnvDTE.ProjectItem GetProjectItemWithName(EnvDTE.ProjectItems items, string itemName) { return GetAllProjectItemsRecursive(items).Cast().Where(i=>i.Name == itemName).First(); } public static string GetProjectItemFullPath(EnvDTE.ProjectItem item) { return item.Properties.Item("FullPath").Value.ToString(); } public static IEnumerable GetAllSolutionItems(EnvDTE.DTE dte) { List itemList = new List(); foreach (Project item in GetAllProjects(dte)) { if (item == null || item.ProjectItems == null) continue; itemList.AddRange(GetAllProjectItemsRecursive(item.ProjectItems)); } return itemList; } public static IEnumerable GetAllProjectItemsRecursive(EnvDTE.ProjectItems projectItems) { foreach (EnvDTE.ProjectItem projectItem in projectItems) { if (projectItem.ProjectItems == null) continue; foreach (EnvDTE.ProjectItem subItem in GetAllProjectItemsRecursive(projectItem.ProjectItems)) { yield return subItem; } yield return projectItem; } } } public sealed class OutputFile { public OutputFile() { this.FileProperties = new FileProperties { CustomTool = String.Empty, BuildAction = BuildAction.None }; } public string FileName { get; set; } public string ProjectName { get; set; } public string FolderName { get; set; } public string Content { get; set; } public FileProperties FileProperties { get; set; } } public class BuildAction { public const string None = "None"; public const string Compile = "Compile"; public const string Content = "Content"; public const string EmbeddedResource = "EmbeddedResource"; public const string EntityDeploy = "EntityDeploy"; } public sealed class FileProperties { public FileProperties () { this.TemplateParameter = new Dictionary(); } public string CustomTool { get; set; } public string BuildAction { get; set; } public Dictionary TemplateParameter { get; set; } internal string BuildActionString { get { return this.BuildAction; } } } #>