From 3b28446cecf2cb5ca8baf10f559f58e25858194a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 25 May 2008 15:38:58 +0000 Subject: [PATCH] Add class for each code snippet conversion. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3088 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Src/Project/CSharpToVBNetConverter.cs | 5 +- .../Project/Extensions/GridAdornerProvider.cs | 4 +- .../NRefactory/Project/Src/SnippetParser.cs | 57 +++--- src/Main/Base/Test/MemberLookupHelperTests.cs | 1 - .../ICSharpCode.SharpDevelop.Dom.csproj | 1 + .../Project/Src/ExtensionMethods.cs | 2 +- .../CSharpToVBNetConvertVisitor.cs | 3 +- .../Src/NRefactoryResolver/CodeConverter.cs | 168 ++++++++++++++++++ .../ProjectContent/ProjectContentRegistry.cs | 7 +- .../CodeSnippetConverterTests.cs | 45 +++++ .../ICSharpCode.SharpDevelop.Dom.Tests.csproj | 1 + 11 files changed, 261 insertions(+), 33 deletions(-) create mode 100644 src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeConverter.cs create mode 100644 src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/CodeSnippetConverterTests.cs diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/CSharpToVBNetConverter.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/CSharpToVBNetConverter.cs index a8c24f4287..388cff457e 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/CSharpToVBNetConverter.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/CSharpToVBNetConverter.cs @@ -52,7 +52,7 @@ namespace VBNetBinding CSharpToVBNetConvertVisitor visitor = new CSharpToVBNetConvertVisitor(pc, ParserService.GetParseInformation(sourceItem.FileName)); visitor.RootNamespaceToRemove = sourceItem.Project.RootNamespace; visitor.DefaultImportsToRemove = defaultImports; - visitor.StartupObjectToMakePublic = startupObject; + visitor.StartupObjectToMakePublic = startupObject; compilationUnit.AcceptVisitor(visitor, null); } @@ -60,8 +60,7 @@ namespace VBNetBinding { VBNetProject vbProject = (VBNetProject)targetProject; base.CopyProperties(sourceProject, targetProject); - FixProperty(vbProject, "DefineConstants", - delegate(string v) { return v.Replace(';', ','); }); + FixProperty(vbProject, "DefineConstants", v => v.Replace(';', ',')); // determine the StartupObject startupObject = vbProject.GetEvaluatedProperty("StartupObject"); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/GridAdornerProvider.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/GridAdornerProvider.cs index 39c47d5be5..5425d321d6 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/GridAdornerProvider.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/GridAdornerProvider.cs @@ -92,7 +92,7 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions readonly List splitterList = new List(); /// - /// flag used to unsure that the asynchronus splitter creation is only enqueued once + /// flag used to ensure that the asynchronus splitter creation is only enqueued once /// bool requireSplitterRecreation; @@ -101,7 +101,7 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions if (requireSplitterRecreation) return; requireSplitterRecreation = true; - // splitter creation is asynchronous to prevent unnecessary splitter re-creation when multiple + // splitter creation is delayed to prevent unnecessary splitter re-creation when multiple // changes to the collection are done. // It also ensures that the Offset property of new rows/columns is initialized when the splitter // is added. diff --git a/src/Libraries/NRefactory/Project/Src/SnippetParser.cs b/src/Libraries/NRefactory/Project/Src/SnippetParser.cs index 048268d912..f3bc5ba2d4 100644 --- a/src/Libraries/NRefactory/Project/Src/SnippetParser.cs +++ b/src/Libraries/NRefactory/Project/Src/SnippetParser.cs @@ -13,6 +13,15 @@ using ICSharpCode.NRefactory.Parser; namespace ICSharpCode.NRefactory { + public enum SnippetType + { + None, + CompilationUnit, + Expression, + Statements, + TypeMembers + } + /// /// The snippet parser supports parsing code snippets that are not valid as a full compilation unit. /// @@ -25,22 +34,20 @@ namespace ICSharpCode.NRefactory this.language = language; } - Errors errors; - List specials; - /// /// Gets the errors of the last call to Parse(). Returns null if parse was not yet called. /// - public Errors Errors { - get { return errors; } - } + public Errors Errors { get; private set; } /// /// Gets the specials of the last call to Parse(). Returns null if parse was not yet called. /// - public List Specials { - get { return specials; } - } + public List Specials { get; private set; } + + /// + /// Gets the snippet type of the last call to Parse(). Returns None if parse was not yet called. + /// + public SnippetType SnippetType { get; private set; } /// /// Parse the code. The result may be a CompilationUnit, an Expression, a list of statements or a list of class @@ -50,11 +57,12 @@ namespace ICSharpCode.NRefactory { IParser parser = ParserFactory.CreateParser(language, new StringReader(code)); parser.Parse(); - errors = parser.Errors; - specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + this.Errors = parser.Errors; + this.Specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + this.SnippetType = SnippetType.CompilationUnit; INode result = parser.CompilationUnit; - if (errors.Count > 0) { + if (this.Errors.Count > 0) { if (language == SupportedLanguage.CSharp) { // SEMICOLON HACK : without a trailing semicolon, parsing expressions does not work correctly parser = ParserFactory.CreateParser(language, new StringReader(code + ";")); @@ -62,27 +70,30 @@ namespace ICSharpCode.NRefactory parser = ParserFactory.CreateParser(language, new StringReader(code)); } Expression expression = parser.ParseExpression(); - if (expression != null && parser.Errors.Count < errors.Count) { - errors = parser.Errors; - specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + if (expression != null && parser.Errors.Count < this.Errors.Count) { + this.Errors = parser.Errors; + this.Specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + this.SnippetType = SnippetType.Expression; result = expression; } } - if (errors.Count > 0) { + if (this.Errors.Count > 0) { parser = ParserFactory.CreateParser(language, new StringReader(code)); BlockStatement block = parser.ParseBlock(); - if (block != null && parser.Errors.Count < errors.Count) { - errors = parser.Errors; - specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + if (block != null && parser.Errors.Count < this.Errors.Count) { + this.Errors = parser.Errors; + this.Specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + this.SnippetType = SnippetType.Statements; result = block; } } - if (errors.Count > 0) { + if (this.Errors.Count > 0) { parser = ParserFactory.CreateParser(language, new StringReader(code)); List members = parser.ParseTypeMembers(); - if (members != null && members.Count > 0 && parser.Errors.Count < errors.Count) { - errors = parser.Errors; - specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + if (members != null && members.Count > 0 && parser.Errors.Count < this.Errors.Count) { + this.Errors = parser.Errors; + this.Specials = parser.Lexer.SpecialTracker.RetrieveSpecials(); + this.SnippetType = SnippetType.TypeMembers; result = new NodeListNode(members); } } diff --git a/src/Main/Base/Test/MemberLookupHelperTests.cs b/src/Main/Base/Test/MemberLookupHelperTests.cs index b572fb05d2..7a613a2f88 100644 --- a/src/Main/Base/Test/MemberLookupHelperTests.cs +++ b/src/Main/Base/Test/MemberLookupHelperTests.cs @@ -28,7 +28,6 @@ namespace ICSharpCode.SharpDevelop.Tests ProjectContentRegistry r = new ProjectContentRegistry(); msc = r.Mscorlib; swf = r.GetProjectContentForReference("System.Windows.Forms", "System.Windows.Forms"); - ((ReflectionProjectContent)swf).InitializeReferences(); DefaultProjectContent dpc = new DefaultProjectContent(); dpc.ReferencedContents.Add(msc); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj index db6cb0bb30..1f2eaae87e 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj @@ -101,6 +101,7 @@ + diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs index 2a04e547a1..b392ca692d 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ExtensionMethods.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.SharpDevelop.Dom arrayList.Add(o); } - public static void AddRange(this IList list, IEnumerable elements) + public static void AddRange(this ICollection list, IEnumerable elements) { foreach (T o in elements) list.Add(o); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs index 27c884931c..1b491aaca6 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs @@ -174,7 +174,8 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver if (memberNode != null && memberNode.InterfaceImplementations.Count > 0) { foreach (InterfaceImplementation impl in memberNode.InterfaceImplementations) { if (impl.MemberName == interfaceMember.Name - && object.Equals(ResolveType(impl.InterfaceType), interfaceReference)) { + && object.Equals(ResolveType(impl.InterfaceType), interfaceReference)) + { return true; } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeConverter.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeConverter.cs new file mode 100644 index 0000000000..33920fb97a --- /dev/null +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CodeConverter.cs @@ -0,0 +1,168 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.NRefactory.PrettyPrinter; +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; + +namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver +{ + /// + /// Allows converting code snippets between C# and VB. + /// This class isn't used by SharpDevelop itself (because it doesn't support projects). + /// It works by creating a dummy project for the file to convert with a set of default references. + /// + public class CodeSnippetConverter + { + /// + /// Project-wide imports to add to all files when converting VB to C#. + /// + public IList DefaultImportsToAdd = new List { "Microsoft.VisualBasic", "System", "System.Collections", "System.Collections.Generic", "System.Data", "System.Diagnostics" }; + + /// + /// Imports to remove (because they will become project-wide imports) when converting C# to VB. + /// + public IList DefaultImportsToRemove = new List { "Microsoft.VisualBasic", "System" }; + + /// + /// References project contents, for resolving type references during the conversion. + /// + public IList ReferencedContents = new List(); + + DefaultProjectContent project; + List specials; + CompilationUnit compilationUnit; + ParseInformation parseInfo; + + #region Parsing + INode Parse(SupportedLanguage sourceLanguage, string sourceCode, out string error) + { + project = new DefaultProjectContent(); + project.ReferencedContents.AddRange(ReferencedContents); + if (sourceLanguage == SupportedLanguage.VBNet) { + project.DefaultImports = new DefaultUsing(project); + project.DefaultImports.Usings.AddRange(DefaultImportsToAdd); + } + SnippetParser parser = new SnippetParser(sourceLanguage); + INode result = parser.Parse(sourceCode); + error = parser.Errors.ErrorOutput; + specials = parser.Specials; + if (parser.Errors.Count != 0) + return null; + + // now create a dummy compilation unit around the snippet result + switch (parser.SnippetType) { + case SnippetType.CompilationUnit: + compilationUnit = (CompilationUnit)result; + break; + case SnippetType.Expression: + compilationUnit = MakeCompilationUnitFromTypeMembers( + MakeMethodFromBlock( + MakeBlockFromExpression( + (Expression)result + ))); + break; + case SnippetType.Statements: + compilationUnit = MakeCompilationUnitFromTypeMembers( + MakeMethodFromBlock( + (BlockStatement)result + )); + break; + case SnippetType.TypeMembers: + compilationUnit = MakeCompilationUnitFromTypeMembers(result.Children); + break; + default: + throw new NotSupportedException("Unknown snippet type: " + parser.SnippetType); + } + + // convert NRefactory CU in DOM CU + NRefactoryASTConvertVisitor visitor = new NRefactoryASTConvertVisitor(project); + visitor.VisitCompilationUnit(compilationUnit, null); + visitor.Cu.FileName = sourceLanguage == SupportedLanguage.CSharp ? "a.cs" : "a.vb"; + + // and register the compilation unit in the DOM + foreach (IClass c in visitor.Cu.Classes) { + project.AddClassToNamespaceList(c); + } + parseInfo = new ParseInformation(); + parseInfo.SetCompilationUnit(visitor.Cu); + + return result; + } + + BlockStatement MakeBlockFromExpression(Expression expr) + { + return new BlockStatement { + Children = { + new ExpressionStatement(expr) + } + }; + } + + INode[] MakeMethodFromBlock(BlockStatement block) + { + return new INode[] { + new MethodDeclaration { + Name = "DummyMethodForConversion", + Body = block + } + }; + } + + CompilationUnit MakeCompilationUnitFromTypeMembers(IEnumerable members) + { + TypeDeclaration type = new TypeDeclaration(Modifiers.None, null) { + Name = "DummyTypeForConversion" + }; + type.Children.AddRange(members); + return new CompilationUnit { + Children = { + type + } + }; + } + #endregion + + public string CSharpToVB(string input, out string errors) + { + INode node = Parse(SupportedLanguage.CSharp, input, out errors); + if (node == null) + return null; + // apply conversion logic: + compilationUnit.AcceptVisitor( + new CSharpToVBNetConvertVisitor(project, parseInfo) { + DefaultImportsToRemove = DefaultImportsToRemove, + }, + null); + PreprocessingDirective.CSharpToVB(specials); + return CreateCode(node, new VBNetOutputVisitor()); + } + + public string VBToCSharp(string input, out string errors) + { + INode node = Parse(SupportedLanguage.VBNet, input, out errors); + if (node == null) + return null; + // apply conversion logic: + compilationUnit.AcceptVisitor( + new VBNetToCSharpConvertVisitor(project, parseInfo), + null); + PreprocessingDirective.VBToCSharp(specials); + return CreateCode(node, new CSharpOutputVisitor()); + } + + string CreateCode(INode node, IOutputAstVisitor outputVisitor) + { + using (SpecialNodesInserter.Install(specials, outputVisitor)) { + node.AcceptVisitor(outputVisitor, null); + } + return outputVisitor.Text; + } + } +} diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs index 6171317780..adfe68c97f 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ProjectContentRegistry.cs @@ -273,8 +273,11 @@ namespace ICSharpCode.SharpDevelop.Dom if (pc != null) { ReflectionProjectContent reflectionProjectContent = pc as ReflectionProjectContent; - if (reflectionProjectContent != null && reflectionProjectContent.AssemblyFullName != null) { - contents[reflectionProjectContent.AssemblyFullName] = pc; + if (reflectionProjectContent != null) { + reflectionProjectContent.InitializeReferences(); + if (reflectionProjectContent.AssemblyFullName != null) { + contents[reflectionProjectContent.AssemblyFullName] = pc; + } } contents[itemInclude] = pc; contents[itemFileName] = pc; diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/CodeSnippetConverterTests.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/CodeSnippetConverterTests.cs new file mode 100644 index 0000000000..870eb73966 --- /dev/null +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/CodeSnippetConverterTests.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace ICSharpCode.SharpDevelop.Dom.Tests +{ + [TestFixture] + public class CodeSnippetConverterTests + { + IProjectContent mscorlib, system; + CodeSnippetConverter converter; + string errors; + + public CodeSnippetConverterTests() + { + ProjectContentRegistry pcr = new ProjectContentRegistry(); + mscorlib = pcr.Mscorlib; + system = pcr.GetProjectContentForReference("System", "System"); + } + + [SetUp] + public void SetUp() + { + converter = new CodeSnippetConverter { + ReferencedContents = { + mscorlib, system + } + }; + } + + [Test] + public void FixExpressionCase() + { + Assert.AreEqual("AppDomain.CurrentDomain", converter.VBToCSharp("appdomain.currentdomain", out errors)); + } + } +} diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj index 62f62407ee..5f11eec378 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj @@ -44,6 +44,7 @@ +