Browse Source

Add Custom Tools support.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2020 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 19 years ago
parent
commit
f3939e3257
  1. 4
      AddIns/ICSharpCode.SharpDevelop.addin
  2. 67
      src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditor.cs
  3. 1
      src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/GuidEditor.cs
  4. 2
      src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/WixXmlAttributePropertyDescriptor.cs
  5. 1
      src/AddIns/BackendBindings/WixBinding/Project/WixBinding.csproj
  6. 6
      src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.addin
  7. 7
      src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.csproj
  8. 57
      src/AddIns/DisplayBindings/ResourceEditor/Project/Src/ResourceCodeGeneratorTool.cs
  9. 76
      src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsDocument.cs
  10. 1
      src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEditor.csproj
  11. 9
      src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEntry.cs
  12. 2
      src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsView.Designer.cs
  13. 39
      src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsViewContent.cs
  14. 13
      src/AddIns/DisplayBindings/SettingsEditor/Project/SpecialTypes.cs
  15. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  16. 17
      src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs
  17. 26
      src/Main/Base/Project/Src/Gui/IProgressMonitor.cs
  18. 5
      src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategory.cs
  19. 8
      src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs
  20. 496
      src/Main/Base/Project/Src/Project/CustomTool.cs
  21. 20
      src/Main/Base/Project/Src/Project/Items/FileProjectItem.cs
  22. 4
      src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
  23. 25
      src/Main/Base/Project/Src/Util/Linq.cs
  24. 78
      src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditor.cs
  25. 24
      src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditorListBox.cs
  26. 3
      src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ICSharpCode.SharpDevelop.Widgets.csproj

4
AddIns/ICSharpCode.SharpDevelop.addin

@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
<ConditionEvaluator name="CanNavigateForward" class="ICSharpCode.SharpDevelop.CanNavigateForwardConditionEvaluator"/>
<ConditionEvaluator name="CompareProjectProperty" class="ICSharpCode.SharpDevelop.CompareProjectPropertyConditionEvaluator"/>
<Doozer name="CustomTool" class="ICSharpCode.SharpDevelop.Project.CustomToolDoozer"/>
<Doozer name="DialogPanel" class="ICSharpCode.SharpDevelop.DialogPanelDoozer"/>
<Doozer name="DisplayBinding" class="ICSharpCode.SharpDevelop.DisplayBindingDoozer"/>
<Doozer name="Pad" class="ICSharpCode.SharpDevelop.PadDoozer"/>
@ -383,6 +384,9 @@ @@ -383,6 +384,9 @@
<MenuItem id = "ExcludeFile"
label = "${res:ProjectComponent.ContextMenu.ExcludeFileFromProject}"
class = "ICSharpCode.SharpDevelop.Project.Commands.ExcludeFileFromProject"/>
<MenuItem id = "ExecuteCustomTool"
label = "Execute custom tool"
class = "ICSharpCode.SharpDevelop.Project.ExecuteCustomToolCommand"/>
</Condition>
<Condition name = "Ownerstate" ownerstate = "None">

67
src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditor.cs

@ -6,76 +6,31 @@ @@ -6,76 +6,31 @@
// </file>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport;
namespace ICSharpCode.WixBinding
{
public class DropDownEditor : UITypeEditor
public class WixDropDownEditor : DropDownEditor
{
/// <summary>
/// Returns the drop down style.
/// </summary>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
protected override Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService)
{
return UITypeEditorEditStyle.DropDown;
return new DropDownEditorListBox(editorService, GetDropDownItems(context));
}
public override bool IsDropDownResizable {
get {
return false;
}
}
/// <summary>
/// Shows the drop down editor control in the drop down so the user
/// can change the value.
/// </summary>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
IEnumerable<string> GetDropDownItems(ITypeDescriptorContext context)
{
IWindowsFormsEditorService editorService = null;
if (provider != null) {
editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
}
if (editorService != null) {
using (Control control = CreateDropDownControl(context, editorService)) {
SetValue(control, value);
editorService.DropDownControl(control);
value = GetValue(control);
if (context != null) {
WixXmlAttributePropertyDescriptor propertyDescriptor = context.PropertyDescriptor as WixXmlAttributePropertyDescriptor;
if (propertyDescriptor != null && propertyDescriptor.WixXmlAttribute.HasValues) {
return propertyDescriptor.WixXmlAttribute.Values;
}
}
return value;
}
/// <summary>
/// Creates the drop down control.
/// </summary>
protected virtual Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService)
{
return new DropDownEditorListBox(context, editorService);
}
/// <summary>
/// Sets the current value in the drop down control.
/// </summary>
protected virtual void SetValue(Control control, object value)
{
DropDownEditorListBox listBox = (DropDownEditorListBox)control;
listBox.Value = (string)value;
}
/// <summary>
/// Gets the current value from the drop down control.
/// </summary>
protected virtual object GetValue(Control control)
{
DropDownEditorListBox listBox = (DropDownEditorListBox)control;
return listBox.Value;
return new string[0];
}
}
}

1
src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/GuidEditor.cs

@ -9,6 +9,7 @@ using System; @@ -9,6 +9,7 @@ using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport;
namespace ICSharpCode.WixBinding
{

2
src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/WixXmlAttributePropertyDescriptor.cs

@ -118,7 +118,7 @@ namespace ICSharpCode.WixBinding @@ -118,7 +118,7 @@ namespace ICSharpCode.WixBinding
break;
case WixXmlAttributeType.Text:
if (wixXmlAttribute.HasValues) {
attributes.Add(new EditorAttribute(typeof(DropDownEditor), typeof(UITypeEditor)));
attributes.Add(new EditorAttribute(typeof(WixDropDownEditor), typeof(UITypeEditor)));
}
break;
}

1
src/AddIns/BackendBindings/WixBinding/Project/WixBinding.csproj

@ -142,7 +142,6 @@ @@ -142,7 +142,6 @@
<Compile Include="Src\PropertyGrid\GuidEditorListBox.cs" />
<Compile Include="Src\PropertyGrid\RelativeFileNameEditor.cs" />
<Compile Include="Src\PropertyGrid\DropDownEditor.cs" />
<Compile Include="Src\PropertyGrid\DropDownEditorListBox.cs" />
<Compile Include="Src\WixPackageFilesDiff.cs" />
<Compile Include="Src\WixPackageFilesDiffResult.cs" />
<Compile Include="Src\DirectoryReader.cs" />

6
src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.addin

@ -62,4 +62,10 @@ @@ -62,4 +62,10 @@
<Include id = "SelectAll" item = "/SharpDevelop/Workbench/MainMenu/Edit/SelectAll"/>
</Path>
<Path name = "/SharpDevelop/CustomTools">
<CustomTool id = "ResXFileCodeGenerator"
class = "ResourceEditor.ResourceCodeGeneratorTool"
fileNamePattern = "\.res(x|ources)$"/>
</Path>
</AddIn>

7
src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.csproj

@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Design" />
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\AssemblyInfo.cs" />
@ -84,6 +85,7 @@ @@ -84,6 +85,7 @@
<Compile Include="..\..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Configuration\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Src\ResourceCodeGeneratorTool.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
@ -96,6 +98,11 @@ @@ -96,6 +98,11 @@
<Name>ICSharpCode.Core</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj">
<Project>{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}</Project>
<Name>ICSharpCode.SharpDevelop.Dom</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project>

57
src/AddIns/DisplayBindings/ResourceEditor/Project/Src/ResourceCodeGeneratorTool.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using ICSharpCode.SharpDevelop.Project;
using System.Resources;
using System.Resources.Tools;
using System.IO;
namespace ResourceEditor
{
public class ResourceCodeGeneratorTool : ICustomTool
{
public void GenerateCode(FileProjectItem item, CustomToolContext context)
{
/*context.GenerateCodeDomAsync(item, context.GetOutputFileName(item, ".Designer"),
delegate {
return GenerateCodeDom();
});*/
string inputFilePath = item.FileName;
IResourceReader reader;
if (Path.GetExtension(inputFilePath) == ".resx") {
reader = new ResXResourceReader(inputFilePath);
} else {
reader = new ResourceReader(inputFilePath);
}
Hashtable resources = new Hashtable();
foreach (DictionaryEntry de in reader) {
resources.Add(de.Key, de.Value);
}
string[] unmatchable = null;
context.WriteCodeDomToFile(
item,
context.GetOutputFileName(item, ".Designer"),
StronglyTypedResourceBuilder.Create(
resources, // resourceList
"Resources", // baseName
context.OutputNamespace, // generatedCodeNamespace
context.OutputNamespace, // resourcesNamespace
context.Project.LanguageProperties.CodeDomProvider, // codeProvider
true, // internal class
out unmatchable
));
}
}
}

76
src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsDocument.cs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Xml;
using System.Collections.Generic;
namespace ICSharpCode.SettingsEditor
{
public class SettingsDocument
{
string generatedClassNamespace = "";
string generatedClassName = "";
List<SettingsEntry> entries = new List<SettingsEntry>();
public string GeneratedClassNamespace {
get { return generatedClassNamespace; }
set { generatedClassNamespace = value ?? ""; }
}
public string GeneratedClassName {
get { return generatedClassName; }
set { generatedClassName = value ?? ""; }
}
public List<SettingsEntry> Entries {
get { return entries; }
}
public SettingsDocument()
{
}
const string XmlNamespace = "http://schemas.microsoft.com/VisualStudio/2004/01/settings";
public SettingsDocument(XmlElement settingsFile, ISettingsEntryHost host)
{
generatedClassNamespace = settingsFile.GetAttribute("GeneratedClassNamespace");
generatedClassName = settingsFile.GetAttribute("GeneratedClassName");
XmlElement settings = settingsFile["Settings"];
foreach (XmlNode node in settings.ChildNodes) {
if (node is XmlElement) {
entries.Add(new SettingsEntry(host, node as XmlElement));
}
}
}
public void Save(XmlWriter writer)
{
writer.WriteStartElement("SettingsFile", XmlNamespace);
writer.WriteAttributeString("CurrentProfile", "(Default)");
writer.WriteAttributeString("GeneratedClassNamespace", generatedClassNamespace);
writer.WriteAttributeString("GeneratedClassName", generatedClassName);
writer.WriteStartElement("Profiles");
writer.WriteStartElement("Profile");
writer.WriteAttributeString("Name", "(Default)");
writer.WriteEndElement(); // Profile
writer.WriteEndElement(); // Profiles
writer.WriteStartElement("Settings");
foreach (SettingsEntry e in entries) {
e.WriteTo(writer);
}
writer.WriteEndElement(); // Settings
writer.WriteEndElement(); // SettingsFile
}
}
}

1
src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEditor.csproj

@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
<Compile Include="SettingsEntryPropertyGridWrapper.cs" />
<Compile Include="ISettingsEntryHost.cs" />
<Compile Include="SpecialTypes.cs" />
<Compile Include="SettingsDocument.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">

9
src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEntry.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using System.Configuration;
@ -70,13 +71,7 @@ namespace ICSharpCode.SettingsEditor @@ -70,13 +71,7 @@ namespace ICSharpCode.SettingsEditor
scope = SettingScope.User;
}
type = GetType(element.GetAttribute("Type"));
if (type != null && type != typeof(string)) {
SettingsPropertyValue v = GetSettingConverter(type, name);
v.SerializedValue = element["Value"].InnerText;
this.value = v.PropertyValue;
} else {
this.value = element["Value"].InnerText;
}
this.SerializedValue = element["Value"].InnerText;
}
static SettingsPropertyValue GetSettingConverter(Type type, string name)

2
src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsView.Designer.cs generated

@ -97,6 +97,7 @@ namespace ICSharpCode.SettingsEditor @@ -97,6 +97,7 @@ namespace ICSharpCode.SettingsEditor
this.TypeColumn.HeaderText = "Type";
this.TypeColumn.MinimumWidth = 50;
this.TypeColumn.Name = "TypeColumn";
this.TypeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
//
// ScopeColumn
//
@ -105,6 +106,7 @@ namespace ICSharpCode.SettingsEditor @@ -105,6 +106,7 @@ namespace ICSharpCode.SettingsEditor
this.ScopeColumn.HeaderText = "Scope";
this.ScopeColumn.MinimumWidth = 30;
this.ScopeColumn.Name = "ScopeColumn";
this.ScopeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
//
// ValueColumn
//

39
src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsViewContent.cs

@ -23,6 +23,7 @@ namespace ICSharpCode.SettingsEditor @@ -23,6 +23,7 @@ namespace ICSharpCode.SettingsEditor
{
SettingsView view = new SettingsView();
PropertyContainer propertyContainer = new PropertyContainer();
SettingsDocument setDoc = new SettingsDocument();
public SettingsViewContent()
{
@ -48,43 +49,35 @@ namespace ICSharpCode.SettingsEditor @@ -48,43 +49,35 @@ namespace ICSharpCode.SettingsEditor
try {
XmlDocument doc = new XmlDocument();
doc.Load(filename);
XmlElement settings = doc.DocumentElement["Settings"];
List<SettingsEntry> entries = new List<SettingsEntry>();
foreach (XmlNode node in settings.ChildNodes) {
if (node is XmlElement) {
entries.Add(new SettingsEntry(view, node as XmlElement));
}
}
view.ShowEntries(entries);
setDoc = new SettingsDocument(doc.DocumentElement, view);
view.ShowEntries(setDoc.Entries);
} catch (XmlException ex) {
MessageService.ShowMessage(ex.Message);
ShowLoadError(ex.Message);
}
IsDirty = false;
}
const string XmlNamespace = "http://schemas.microsoft.com/VisualStudio/2004/01/settings";
void ShowLoadError(string message)
{
MessageService.ShowMessage(message);
if (this.WorkbenchWindow != null) {
this.WorkbenchWindow.CloseWindow(true);
}
}
public override void Save(string fileName)
{
using (XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) {
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument();
writer.WriteStartElement("SettingsFile", XmlNamespace);
writer.WriteAttributeString("CurrentProfile", "(Default)");
writer.WriteStartElement("Profiles");
writer.WriteStartElement("Profile");
writer.WriteAttributeString("Name", "(Default)");
writer.WriteEndElement(); // Profile
writer.WriteEndElement(); // Profiles
setDoc.Entries.Clear();
setDoc.Entries.AddRange(view.GetAllEntries());
writer.WriteStartElement("Settings");
foreach (SettingsEntry e in view.GetAllEntries()) {
e.WriteTo(writer);
}
writer.WriteEndElement(); // Settings
setDoc.Save(writer);
writer.WriteEndElement(); // SettingsFile
writer.WriteEndDocument();
}
IsDirty = false;

13
src/AddIns/DisplayBindings/SettingsEditor/Project/SpecialTypes.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Configuration;
namespace ICSharpCode.SettingsEditor
{
@ -15,11 +16,13 @@ namespace ICSharpCode.SettingsEditor @@ -15,11 +16,13 @@ namespace ICSharpCode.SettingsEditor
{
internal string name;
internal Type type;
internal SpecialSetting specialSetting;
internal SpecialTypeDescriptor(string name, Type type)
internal SpecialTypeDescriptor(string name, Type type, SpecialSetting specialSetting)
{
this.name = name;
this.type = type;
this.specialSetting = specialSetting;
}
public string GetString(object value)
@ -38,8 +41,12 @@ namespace ICSharpCode.SettingsEditor @@ -38,8 +41,12 @@ namespace ICSharpCode.SettingsEditor
}
internal static readonly SpecialTypeDescriptor[] Descriptors = {
new SpecialTypeDescriptor("(Web Service URL)", typeof(WebServiceUrlDummyType)),
new SpecialTypeDescriptor("(Connection string)", typeof(ConnectionStringDummyType)),
new SpecialTypeDescriptor("(Web Service URL)",
typeof(WebServiceUrlDummyType),
SpecialSetting.WebServiceUrl),
new SpecialTypeDescriptor("(Connection string)",
typeof(ConnectionStringDummyType),
SpecialSetting.ConnectionString)
};
}

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -688,6 +688,7 @@ @@ -688,6 +688,7 @@
<Compile Include="Src\Project\MSBuildImport.cs" />
<Compile Include="Src\Util\Linq.cs" />
<Compile Include="Src\Util\DebugTimer.cs" />
<Compile Include="Src\Project\CustomTool.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\DockPanel_Src\WinFormsUI\WinFormsUI.csproj">

17
src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs

@ -396,7 +396,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -396,7 +396,7 @@ namespace ICSharpCode.SharpDevelop.Gui
createdFiles.Add(new KeyValuePair<string, PropertyGroup>(parsedFileName, newfile.CreateMSBuildProperties()));
}
string GenerateValidClassName(string className)
internal static string GenerateValidClassName(string className)
{
int idx = 0;
while (idx < className.Length && className[idx] != '_' && !Char.IsLetter(className[idx])) {
@ -449,20 +449,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -449,20 +449,7 @@ namespace ICSharpCode.SharpDevelop.Gui
fileName = Path.GetFullPath(fileName);
IProject project = ProjectService.CurrentProject;
if (project != null) {
string relPath = FileUtility.GetRelativePath(project.Directory, Path.GetDirectoryName(fileName));
string[] subdirs = relPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
StringBuilder standardNameSpace = new StringBuilder(project.RootNamespace);
foreach(string subdir in subdirs) {
if (subdir == "." || subdir == ".." || subdir.Length == 0)
continue;
if (subdir.Equals("src", StringComparison.OrdinalIgnoreCase))
continue;
if (subdir.Equals("source", StringComparison.OrdinalIgnoreCase))
continue;
standardNameSpace.Append('.');
standardNameSpace.Append(GenerateValidClassName(subdir));
}
StringParser.Properties["StandardNamespace"] = standardNameSpace.ToString();
StringParser.Properties["StandardNamespace"] = CustomToolsService.GetDefaultNamespace(project, fileName);
}
}
StringParser.Properties["FullName"] = fileName;

26
src/Main/Base/Project/Src/Gui/IProgressMonitor.cs

@ -27,4 +27,30 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -27,4 +27,30 @@ namespace ICSharpCode.SharpDevelop.Gui
set;
}
}
internal class DummyProgressMonitor : IProgressMonitor
{
int workDone;
string taskName;
public int WorkDone {
get { return workDone; }
set { workDone = value; }
}
public string TaskName {
get { return taskName; }
set { taskName = value; }
}
public void BeginTask(string name, int totalWork)
{
taskName = name;
workDone = 0;
}
public void Done()
{
}
}
}

5
src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategory.cs

@ -50,6 +50,11 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -50,6 +50,11 @@ namespace ICSharpCode.SharpDevelop.Gui
this.displayCategory = displayCategory;
}
public void AppendLine(string text)
{
AppendText(text + Environment.NewLine);
}
public void AppendText(string text)
{
lock (textBuilder) {

8
src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs

@ -71,6 +71,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -71,6 +71,7 @@ namespace ICSharpCode.SharpDevelop.Gui
StatusBarService.Initialize();
DomHostCallback.Register(); // must be called after StatusBarService.Initialize()
ParserService.InitializeParserService();
Project.CustomToolsService.Initialize();
workbench = new DefaultWorkbench();
MessageService.MainForm = workbench;
@ -125,6 +126,13 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -125,6 +126,13 @@ namespace ICSharpCode.SharpDevelop.Gui
}
}
internal static void AssertMainThread()
{
if (InvokeRequired) {
throw new InvalidOperationException("This operation can be called on the main thread only.");
}
}
/// <summary>
/// Makes a call GUI threadsafe. WARNING: This method waits for the result of the
/// operation, which can result in a dead-lock when the main thread waits for a lock

496
src/Main/Base/Project/Src/Project/CustomTool.cs

@ -0,0 +1,496 @@ @@ -0,0 +1,496 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.SharpDevelop.Project
{
/// <summary>
/// Registered in /SharpDevelop/CustomTools/
/// </summary>
public interface ICustomTool
{
void GenerateCode(FileProjectItem item, CustomToolContext context);
}
#region CustomToolContext
/// <summary>
/// Provides ProgressMonitor and MessageView to custom tools.
/// Also provides helper methods that are useful for custom tools.
/// </summary>
public sealed class CustomToolContext
{
IProject project;
IProgressMonitor progressMonitor;
string outputNamespace;
internal bool RunningSeparateThread;
public CustomToolContext(IProject project)
: this(project, new DummyProgressMonitor())
{
}
public CustomToolContext(IProject project, IProgressMonitor progressMonitor)
{
if (project == null)
throw new ArgumentNullException("project");
this.project = project;
this.ProgressMonitor = progressMonitor;
}
/// <summary>
/// Returns the project the custom tool is being run for. The IProject interface
/// is not thread-safe!
/// </summary>
public IProject Project {
get { return project; }
}
public string OutputNamespace {
get { return outputNamespace; }
set { outputNamespace = value; }
}
/// <summary>
/// Runs a method asynchronously. Prevents another CustomTool invocation
/// on the same file while action is running.
/// </summary>
public void RunAsync(Action action)
{
RunningSeparateThread = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate {
try {
action();
} catch (Exception ex) {
MessageService.ShowError(ex);
} finally {
CustomToolsService.NotifyAsyncFinish(this);
}
});
}
static object lockObject = new object();
static volatile MessageViewCategory customToolMessageView;
internal static MessageViewCategory StaticMessageView {
get {
if (customToolMessageView == null) {
lock (lockObject) {
if (customToolMessageView == null) {
customToolMessageView = new MessageViewCategory("Custom Tool");
CompilerMessageView.Instance.AddCategory(customToolMessageView);
}
}
}
return customToolMessageView;
}
}
/// <summary>
/// Returns the message view where custom tools can write to. This member is thread-safe.
/// </summary>
public MessageViewCategory MessageView {
get {
return StaticMessageView;
}
}
public string GetOutputFileName(FileProjectItem baseItem, string additionalExtension)
{
if (baseItem == null)
throw new ArgumentNullException("baseItem");
if (baseItem.Project != project)
throw new ArgumentException("baseItem is not from project this CustomToolContext belongs to");
string newExtension = null;
if (project.LanguageProperties.CodeDomProvider != null) {
newExtension = project.LanguageProperties.CodeDomProvider.FileExtension;
}
if (string.IsNullOrEmpty(newExtension)) {
if (string.IsNullOrEmpty(additionalExtension)) {
newExtension = ".unknown";
} else {
newExtension = additionalExtension;
additionalExtension = "";
}
}
if (!newExtension.StartsWith(".")) {
newExtension = "." + newExtension;
}
return Path.ChangeExtension(baseItem.FileName, additionalExtension + newExtension);
}
public FileProjectItem EnsureOutputFileIsInProject(FileProjectItem baseItem, string outputFileName)
{
WorkbenchSingleton.AssertMainThread();
FileProjectItem outputItem = CustomToolsService.FindProjectItem(project, outputFileName);
if (outputItem == null) {
outputItem = new FileProjectItem(project, ItemType.Compile);
outputItem.FileName = outputFileName;
outputItem.DependentUpon = Path.GetFileName(baseItem.FileName);
ProjectService.AddProjectItem(project, outputItem);
ProjectBrowserPad.Instance.ProjectBrowserControl.RefreshView();
}
return outputItem;
}
public void WriteCodeDomToFile(FileProjectItem baseItem, string outputFileName, CodeCompileUnit ccu)
{
WorkbenchSingleton.AssertMainThread();
CodeDomProvider provider = project.LanguageProperties.CodeDomProvider;
CodeGeneratorOptions options = new CodeDOMGeneratorUtility().CreateCodeGeneratorOptions;
NamedFileOperationDelegate method = delegate(string fileName) {
using (StreamWriter writer = new StreamWriter(fileName, false, System.Text.Encoding.UTF8)) {
if (provider == null) {
writer.WriteLine("No CodeDom provider was found for this language.");
} else {
provider.GenerateCodeFromCompileUnit(ccu, writer, options);
}
}
};
FileUtility.ObservedSave(method, outputFileName, FileErrorPolicy.Inform);
EnsureOutputFileIsInProject(baseItem, outputFileName);
}
public void GenerateCodeDomAsync(FileProjectItem baseItem, string outputFileName, Func<CodeCompileUnit> func)
{
RunAsync(delegate {
CodeCompileUnit ccu = func();
WorkbenchSingleton.SafeThreadAsyncCall(WriteCodeDomToFile, baseItem, outputFileName, ccu);
});
}
public IProgressMonitor ProgressMonitor {
get { return progressMonitor; }
set {
if (value == null)
throw new ArgumentNullException("value");
progressMonitor = value;
}
}
}
#endregion
#region CustomToolDescriptor
sealed class CustomToolDescriptor
{
string name;
string fileNamePattern;
string className;
ICustomTool tool;
AddIn addIn;
public string Name {
get { return name; }
}
public ICustomTool Tool {
get {
if (tool == null) {
tool = (ICustomTool)addIn.CreateObject(className);
}
return tool;
}
}
public bool CanRunOnFile(string fileName)
{
if (string.IsNullOrEmpty(fileNamePattern)) // no regex specified
return true;
return Regex.IsMatch(fileName, fileNamePattern, RegexOptions.IgnoreCase);
}
public CustomToolDescriptor(string name, string fileNamePattern, string className, AddIn addIn)
{
this.name = name;
this.fileNamePattern = fileNamePattern;
this.className = className;
this.addIn = addIn;
}
}
#endregion
#region CustomToolDoozer
/// <summary>
/// Creates CustomToolDescriptor objects.
/// </summary>
/// <attribute name="id" use="required">
/// ID used to identify the custom tool.
/// </attribute>
/// <attribute name="class" use="required">
/// Name of the ICustomTool class.
/// </attribute>
/// <attribute name="fileNamePattern" use="optional">
/// Regular expression that specifies the file names for which the custom tool
/// can be used. Example: "\.res(x|ources)$"
/// </attribute>
/// <usage>Only in /SharpDevelop/CustomTools</usage>
/// <returns>
/// An CustomToolDescriptor object that wraps a ICustomTool object.
/// </returns>
/// <example title="Strongly typed resource generator">
/// &lt;Path name = "/SharpDevelop/CustomTools"&gt;
/// &lt;CustomTool id = "ResXFileCodeGenerator"
/// class = "ResourceEditor.ResourceCodeGeneratorTool"
/// fileNamePattern = "\.res(x|ources)$"/&gt;
/// &lt;/Path&gt;
/// </example>
public sealed class CustomToolDoozer : IDoozer
{
/// <summary>
/// Gets if the doozer handles codon conditions on its own.
/// If this property return false, the item is excluded when the condition is not met.
/// </summary>
public bool HandleConditions {
get {
return false;
}
}
/// <summary>
/// Creates an item with the specified sub items. And the current
/// Condition status for this item.
/// </summary>
public object BuildItem(object caller, Codon codon, ArrayList subItems)
{
return new CustomToolDescriptor(codon.Id, codon.Properties["fileNamePattern"],
codon.Properties["class"], codon.AddIn);
}
}
#endregion
#region CustomToolsService
public static class CustomToolsService
{
class CustomToolRun {
internal CustomToolContext context;
internal string file;
internal FileProjectItem baseItem;
internal ICustomTool customTool;
internal bool showMessageBoxOnErrors;
public CustomToolRun(CustomToolContext context, string file, FileProjectItem baseItem, ICustomTool customTool, bool showMessageBoxOnErrors)
{
this.context = context;
this.file = file;
this.baseItem = baseItem;
this.customTool = customTool;
this.showMessageBoxOnErrors = showMessageBoxOnErrors;
}
}
static bool initialized;
static List<CustomToolRun> toolRuns = new List<CustomToolRun>();
static Dictionary<string, CustomToolDescriptor> toolDict;
static List<CustomToolDescriptor> customToolList;
static CustomToolRun activeToolRun;
internal static void Initialize()
{
customToolList = AddInTree.BuildItems<CustomToolDescriptor>("/SharpDevelop/CustomTools", null, false);
toolDict = new Dictionary<string, CustomToolDescriptor>(StringComparer.OrdinalIgnoreCase);
foreach (CustomToolDescriptor desc in customToolList) {
toolDict[desc.Name] = desc;
}
if (!initialized) {
initialized = true;
FileUtility.FileSaved += OnFileSaved;
}
}
static void OnFileSaved(object sender, FileNameEventArgs e)
{
Solution solution = ProjectService.OpenSolution;
if (solution == null) return;
IProject project = solution.FindProjectContainingFile(e.FileName);
if (project == null) return;
FileProjectItem item = FindProjectItem(project, e.FileName);
if (item == null) return;
if (!string.IsNullOrEmpty(item.CustomTool)) {
RunCustomTool(item, false);
}
}
internal static FileProjectItem FindProjectItem(IProject project, string fileName)
{
return Linq.Find(Linq.OfType<FileProjectItem>(project.Items),
delegate(FileProjectItem item) {
return FileUtility.IsEqualFileName(item.FileName, fileName);
});
// return project.Items.OfType<FileProjectItem>().Find(
// item => FileUtility.IsEqualFileName(item.FileName, outputFileName));
}
public static IEnumerable<string> GetCustomToolNames()
{
return customToolList.ConvertAll<string>(delegate(CustomToolDescriptor desc) {
return desc.Name;
});
}
public static IEnumerable<string> GetCompatibleCustomToolNames(FileProjectItem item)
{
string fileName = item.FileName;
foreach (CustomToolDescriptor desc in customToolList) {
if (desc.CanRunOnFile(fileName)) {
yield return desc.Name;
}
}
}
public static ICustomTool GetCustomTool(string name)
{
lock (toolDict) {
CustomToolDescriptor tool;
if (toolDict.TryGetValue(name, out tool))
return tool.Tool;
else
return null;
}
}
/// <summary>
/// Runs the custom tool specified by the base items' CustomTool property on the base item.
/// </summary>
public static void RunCustomTool(FileProjectItem baseItem, bool showMessageBoxOnErrors)
{
if (baseItem == null)
throw new ArgumentNullException("baseItem");
if (string.IsNullOrEmpty(baseItem.CustomTool))
return;
ICustomTool customTool = GetCustomTool(baseItem.CustomTool);
if (customTool == null) {
string message = "Cannot find custom tool '" + baseItem.CustomTool + "'.";
CustomToolContext.StaticMessageView.AppendLine(message);
if (showMessageBoxOnErrors) {
MessageService.ShowError(message);
}
} else {
RunCustomTool(baseItem, customTool, showMessageBoxOnErrors);
}
}
/// <summary>
/// Runs the specified custom tool on the base item.
/// </summary>
public static void RunCustomTool(FileProjectItem baseItem, ICustomTool customTool, bool showMessageBoxOnErrors)
{
if (baseItem == null)
throw new ArgumentNullException("baseItem");
if (customTool == null)
throw new ArgumentNullException("customTool");
WorkbenchSingleton.AssertMainThread();
string fileName = baseItem.FileName;
if (toolRuns.Exists(delegate(CustomToolRun run) {
return FileUtility.IsEqualFileName(run.file, fileName);
}))
{
// file already in queue, do not enqueue it again
return;
}
CustomToolContext context = new CustomToolContext(baseItem.Project);
if (string.IsNullOrEmpty(baseItem.CustomToolNamespace)) {
context.OutputNamespace = GetDefaultNamespace(baseItem.Project, baseItem.FileName);
} else {
context.OutputNamespace = baseItem.CustomToolNamespace;
}
RunCustomTool(new CustomToolRun(context, fileName, baseItem, customTool, showMessageBoxOnErrors));
}
/// <summary>
/// Gets the namespace the file should have in the specified project.
/// </summary>
public static string GetDefaultNamespace(IProject project, string fileName)
{
if (project == null)
throw new ArgumentNullException("project");
if (fileName == null)
throw new ArgumentNullException("fileName");
string relPath = FileUtility.GetRelativePath(project.Directory, Path.GetDirectoryName(fileName));
string[] subdirs = relPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
StringBuilder standardNameSpace = new StringBuilder(project.RootNamespace);
foreach(string subdir in subdirs) {
if (subdir == "." || subdir == ".." || subdir.Length == 0)
continue;
if (subdir.Equals("src", StringComparison.OrdinalIgnoreCase))
continue;
if (subdir.Equals("source", StringComparison.OrdinalIgnoreCase))
continue;
standardNameSpace.Append('.');
standardNameSpace.Append(NewFileDialog.GenerateValidClassName(subdir));
}
return standardNameSpace.ToString();
}
static void RunCustomTool(CustomToolRun run)
{
if (activeToolRun != null) {
toolRuns.Add(run);
} else {
try {
run.customTool.GenerateCode(run.baseItem, run.context);
} catch (Exception ex) {
LoggingService.Error(ex);
run.context.MessageView.AppendLine("Custom tool '" + run.baseItem.CustomTool + "' failed.");
if (run.showMessageBoxOnErrors) {
MessageService.ShowError("Custom tool '" + run.baseItem.CustomTool
+ "'failed:" + Environment.NewLine + ex.ToString());
}
}
if (run.context.RunningSeparateThread) {
activeToolRun = run;
}
}
}
internal static void NotifyAsyncFinish(CustomToolContext context)
{
WorkbenchSingleton.SafeThreadAsyncCall(
delegate {
activeToolRun = null;
CustomToolRun nextRun = toolRuns[0];
toolRuns.RemoveAt(0);
RunCustomTool(nextRun);
});
}
}
#endregion
#region ExecuteCustomToolCommand
public sealed class ExecuteCustomToolCommand : AbstractMenuCommand
{
public override void Run()
{
FileNode node = Owner as FileNode;
if (node != null) {
FileProjectItem item = node.ProjectItem as FileProjectItem;
if (item != null) {
CustomToolsService.RunCustomTool(item, true);
}
}
}
}
#endregion
}

20
src/Main/Base/Project/Src/Project/Items/FileProjectItem.cs

@ -8,9 +8,12 @@ @@ -8,9 +8,12 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport;
namespace ICSharpCode.SharpDevelop.Project
{
@ -67,6 +70,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -67,6 +70,7 @@ namespace ICSharpCode.SharpDevelop.Project
[LocalizedProperty("${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomTool}",
Description ="${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomTool.Description}")]
[Editor(typeof(CustomToolEditor), typeof(UITypeEditor))]
public string CustomTool {
get {
return base.Properties["Generator"];
@ -76,6 +80,19 @@ namespace ICSharpCode.SharpDevelop.Project @@ -76,6 +80,19 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
sealed class CustomToolEditor : DropDownEditor
{
protected override Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService)
{
FileProjectItem item = context.Instance as FileProjectItem;
if (item != null) {
return new DropDownEditorListBox(editorService, CustomToolsService.GetCompatibleCustomToolNames(item));
} else {
return new DropDownEditorListBox(editorService, CustomToolsService.GetCustomToolNames());
}
}
}
[LocalizedProperty("${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomToolNamespace}",
Description ="${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomToolNamespace.Description}")]
public string CustomToolNamespace {
@ -84,6 +101,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -84,6 +101,7 @@ namespace ICSharpCode.SharpDevelop.Project
}
set {
base.Properties["CustomToolNamespace"] = value;
CustomToolsService.RunCustomTool(this, false);
}
}

4
src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs

@ -157,7 +157,9 @@ namespace ICSharpCode.SharpDevelop @@ -157,7 +157,9 @@ namespace ICSharpCode.SharpDevelop
UpdateDefaultImports(project.Items.ToArray());
break;
case ItemType.Compile:
ParserService.EnqueueForParsing(e.ProjectItem.FileName);
if (System.IO.File.Exists(e.ProjectItem.FileName)) {
ParserService.EnqueueForParsing(e.ProjectItem.FileName);
}
break;
}
}

25
src/Main/Base/Project/Src/Util/Linq.cs

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.SharpDevelop
@ -38,5 +39,29 @@ namespace ICSharpCode.SharpDevelop @@ -38,5 +39,29 @@ namespace ICSharpCode.SharpDevelop
yield return element;
}
}
/// <summary>
/// Returns the elements of type T.
/// </summary>
public static IEnumerable<T> OfType<T>(IEnumerable input)
{
foreach (object element in input) {
if (element is T)
yield return (T)element;
}
}
/// <summary>
/// Returns the first element in input for which filter is true.
/// Returns default(T) if no element matches the filter.
/// </summary>
public static T Find<T>(IEnumerable<T> input, Predicate<T> filter)
{
foreach (T element in input) {
if (filter(element))
return element;
}
return default(T);
}
}
}

78
src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditor.cs

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport
{
public abstract class DropDownEditor : UITypeEditor
{
/// <summary>
/// Returns the drop down style.
/// </summary>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override bool IsDropDownResizable {
get {
return false;
}
}
/// <summary>
/// Shows the drop down editor control in the drop down so the user
/// can change the value.
/// </summary>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService editorService = null;
if (provider != null) {
editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
}
if (editorService != null) {
using (Control control = CreateDropDownControl(context, editorService)) {
SetValue(control, value);
editorService.DropDownControl(control);
value = GetValue(control);
}
}
return value;
}
/// <summary>
/// Creates the drop down control.
/// </summary>
protected abstract Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService);
/// <summary>
/// Sets the current value in the drop down control.
/// </summary>
protected virtual void SetValue(Control control, object value)
{
DropDownEditorListBox listBox = (DropDownEditorListBox)control;
listBox.Value = (string)value;
}
/// <summary>
/// Gets the current value from the drop down control.
/// </summary>
protected virtual object GetValue(Control control)
{
DropDownEditorListBox listBox = (DropDownEditorListBox)control;
return listBox.Value;
}
}
}

24
src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditorListBox.cs → src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditorListBox.cs

@ -6,25 +6,31 @@ @@ -6,25 +6,31 @@
// </file>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace ICSharpCode.WixBinding
namespace ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport
{
public class DropDownEditorListBox : ListBox
{
IWindowsFormsEditorService editorService;
string dropDownValue = String.Empty;
WixXmlAttributePropertyDescriptor propertyDescriptor;
IEnumerable<string> dropDownItems;
public DropDownEditorListBox(ITypeDescriptorContext context, IWindowsFormsEditorService editorService)
public DropDownEditorListBox(IWindowsFormsEditorService editorService, IEnumerable<string> dropDownItems)
{
if (editorService == null)
throw new ArgumentNullException("editorService");
if (dropDownItems == null)
throw new ArgumentNullException("dropDownItems");
this.editorService = editorService;
this.dropDownItems = dropDownItems;
BorderStyle = BorderStyle.None;
if (context != null) {
propertyDescriptor = context.PropertyDescriptor as WixXmlAttributePropertyDescriptor;
}
AddDropDownItems();
}
@ -61,10 +67,8 @@ namespace ICSharpCode.WixBinding @@ -61,10 +67,8 @@ namespace ICSharpCode.WixBinding
void AddDropDownItems()
{
if (propertyDescriptor != null && propertyDescriptor.WixXmlAttribute.HasValues) {
foreach (string item in propertyDescriptor.WixXmlAttribute.Values) {
Items.Add(item);
}
foreach (string item in dropDownItems) {
Items.Add(item);
}
}

3
src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ICSharpCode.SharpDevelop.Widgets.csproj

@ -64,12 +64,15 @@ @@ -64,12 +64,15 @@
<Compile Include="SideBar\SideTab.cs" />
<Compile Include="SideBar\SideTabItem.cs" />
<Compile Include="SpinnerControl.cs" />
<Compile Include="DesignTimeSupport\DropDownEditor.cs" />
<Compile Include="DesignTimeSupport\DropDownEditorListBox.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Configuration" />
<Folder Include="TreeGrid" />
<Folder Include="AutoHide" />
<Folder Include="SideBar" />
<Folder Include="DesignTimeSupport" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>
Loading…
Cancel
Save