diff options
Diffstat (limited to 'main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs')
-rw-r--r-- | main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs new file mode 100644 index 0000000000..8f911641af --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs @@ -0,0 +1,302 @@ +// +// CodeGenerationService.cs +// +// Author: +// mkrueger <mkrueger@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using MonoDevelop.Core; +using System.CodeDom; +using MonoDevelop.Projects; +using System.CodeDom.Compiler; +using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Ide; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using ICSharpCode.NRefactory6.CSharp; +using MonoDevelop.Ide.Editor; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Simplification; +using System.Threading; +using Gtk; +using MonoDevelop.Ide.CodeFormatting; +using Microsoft.CodeAnalysis.CSharp.Formatting; +using MonoDevelop.CSharp.Formatting; +using MonoDevelop.Ide.Gui.Content; + +namespace MonoDevelop.Refactoring +{ + public static class CodeGenerationService + { + // public static IUnresolvedMember AddCodeDomMember (MonoDevelop.Projects.Project project, IUnresolvedTypeDefinition type, CodeTypeMember newMember) + // { + // bool isOpen; + // var data = TextFileProvider.Instance.GetTextEditorData (type.Region.FileName, out isOpen); + // var parsedDocument = TypeSystemService.ParseFile (data.FileName, data.MimeType, data.Text); + // + // var insertionPoints = GetInsertionPoints (data, parsedDocument, type); + // + // var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, type, newMember); + // + // var dotNetProject = project as DotNetProject; + // if (dotNetProject == null) { + // LoggingService.LogError ("Only .NET projects are supported."); + // return null; + // } + // + // var generator = dotNetProject.LanguageBinding.GetCodeDomProvider (); + // StringWriter sw = new StringWriter (); + // var options = new CodeGeneratorOptions (); + // options.IndentString = data.GetLineIndent (type.Region.BeginLine) + "\t"; + // if (newMember is CodeMemberMethod) + // options.BracingStyle = "C"; + // generator.GenerateCodeFromMember (newMember, sw, options); + // + // var code = sw.ToString (); + // if (!string.IsNullOrEmpty (code)) + // suitableInsertionPoint.Insert (data, code); + // if (!isOpen) { + // try { + // File.WriteAllText (type.Region.FileName, data.Text); + // } catch (Exception e) { + // LoggingService.LogError (string.Format ("Failed to write file '{0}'.", type.Region.FileName), e); + // MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); + // } + // } + // var newDocument = TypeSystemService.ParseFile (data.FileName, data.MimeType, data.Text); + // return newDocument.ParsedFile.GetMember (suitableInsertionPoint.Location.Line, int.MaxValue); + // } + + public static async Task AddNewMember (Projects.Project project, ITypeSymbol type, Location part, SyntaxNode newMember, CancellationToken cancellationToken = default(CancellationToken)) + { + if (project == null) + throw new ArgumentNullException (nameof (project)); + if (type == null) + throw new ArgumentNullException (nameof (type)); + if (newMember == null) + throw new ArgumentNullException (nameof (newMember)); + if (!type.IsDefinedInSource ()) + throw new ArgumentException ("The given type needs to be defined in source code.", nameof (type)); + + + var ws = TypeSystemService.GetWorkspace (project.ParentSolution); + var projectId = ws.GetProjectId (project); + var docId = ws.GetDocumentId (projectId, part.SourceTree.FilePath); + + var document = ws.GetDocument (docId, cancellationToken); + + var root = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); + var typeDecl = (ClassDeclarationSyntax)root.FindNode (part.SourceSpan); + + // for some reason the reducer doesn't reduce this + var systemVoid = newMember.DescendantNodesAndSelf ().OfType<QualifiedNameSyntax> ().FirstOrDefault (ma => ma.ToString () == "System.Void"); + + if (systemVoid != null) newMember = newMember.ReplaceNode (systemVoid, SyntaxFactory.ParseTypeName ("void")); + + var newRoot = root.ReplaceNode (typeDecl, typeDecl.AddMembers ((MemberDeclarationSyntax)newMember.WithAdditionalAnnotations (Simplifier.Annotation, Formatter.Annotation))); + document = document.WithSyntaxRoot (newRoot); + var policy = project.Policies.Get<CSharpFormattingPolicy> ("text/x-csharp"); + var textPolicy = project.Policies.Get<TextStylePolicy> ("text/x-csharp"); + var projectOptions = policy.CreateOptions (textPolicy); + + document = await Formatter.FormatAsync (document, Formatter.Annotation, projectOptions, cancellationToken).ConfigureAwait (false); + document = await Simplifier.ReduceAsync (document, Simplifier.Annotation, projectOptions, cancellationToken).ConfigureAwait (false); + var text = await document.GetTextAsync (cancellationToken).ConfigureAwait (false); + var newSolution = ws.CurrentSolution.WithDocumentText (docId, text); + ws.TryApplyChanges (newSolution); + } + + readonly static SyntaxAnnotation insertedMemberAnnotation = new SyntaxAnnotation ("INSERTION_ANNOTATAION"); + public static async Task InsertMemberWithCursor (string operation, Projects.Project project, ITypeSymbol type, Location part, SyntaxNode newMember, CancellationToken cancellationToken = default(CancellationToken)) + { + if (operation == null) + throw new ArgumentNullException (nameof (operation)); + if (project == null) + throw new ArgumentNullException (nameof (project)); + if (type == null) + throw new ArgumentNullException (nameof (type)); + if (newMember == null) + throw new ArgumentNullException (nameof (newMember)); + var ws = TypeSystemService.GetWorkspace (project.ParentSolution); + var projectId = ws.GetProjectId (project); + var docId = ws.GetDocumentId (projectId, part.SourceTree.FilePath); + + var document = ws.GetDocument (docId, cancellationToken); + + var root = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); + var typeDecl = (ClassDeclarationSyntax)root.FindNode (part.SourceSpan); + + // for some reason the reducer doesn't reduce this + var systemVoid = newMember.DescendantNodesAndSelf ().OfType<QualifiedNameSyntax> ().FirstOrDefault (ma => ma.ToString () == "System.Void"); + + if (systemVoid != null) newMember = newMember.ReplaceNode (systemVoid, SyntaxFactory.ParseTypeName ("void")); + + var newRoot = root.ReplaceNode (typeDecl, typeDecl.AddMembers ((MemberDeclarationSyntax)newMember.WithAdditionalAnnotations (Simplifier.Annotation, Formatter.Annotation, insertedMemberAnnotation))); + var doc = IdeApp.Workbench.OpenDocument (part.SourceTree.FilePath, project, true); + + var policy = project.Policies.Get<CSharpFormattingPolicy> ("text/x-csharp"); + var textPolicy = project.Policies.Get<TextStylePolicy> ("text/x-csharp"); + var projectOptions = policy.CreateOptions (textPolicy); + + document = document.WithSyntaxRoot (newRoot); + document = await Formatter.FormatAsync (document, Formatter.Annotation, projectOptions, cancellationToken).ConfigureAwait (false); + document = await Simplifier.ReduceAsync (document, Simplifier.Annotation, projectOptions, cancellationToken).ConfigureAwait (false); + + root = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); + + var node = root.GetAnnotatedNodes (insertedMemberAnnotation).Single (); + + Application.Invoke (delegate { + var insertionPoints = InsertionPointService.GetInsertionPoints ( + doc.Editor, + doc.UpdateParseDocument (), + type, + part + ); + var options = new InsertionModeOptions ( + operation, + insertionPoints, + async point => { + if (!point.Success) + return; + var text = node.ToString (); + point.InsertionPoint.Insert (doc.Editor, doc, text); + } + ); + + doc.Editor.StartInsertionMode (options); + }); + } + + //public static Task<bool> InsertMemberWithCursor (string operation, ITypeSymbol type, Location part, SyntaxNode newMember, bool implementExplicit = false) + //{ + // //TODO: Add dialog for inserting position + // AddNewMember (type, part, newMember, implementExplicit); + // return Task.FromResult (true); + //} + // + // public static int CalculateBodyIndentLevel (IUnresolvedTypeDefinition declaringType) + // { + // if (declaringType == null) + // return 0; + // int indentLevel = 1; + // while (declaringType.DeclaringTypeDefinition != null) { + // indentLevel++; + // declaringType = declaringType.DeclaringTypeDefinition; + // } + // var file = declaringType.UnresolvedFile as CSharpUnresolvedFile; + // if (file == null) + // return indentLevel; + // var scope = file.GetUsingScope (declaringType.Region.Begin); + // while (scope != null && !string.IsNullOrEmpty (scope.NamespaceName)) { + // indentLevel++; + // // skip virtual scopes. + // while (scope.Parent != null && scope.Parent.Region == scope.Region) + // scope = scope.Parent; + // scope = scope.Parent; + // } + // return indentLevel; + // } + public static MonoDevelop.Ide.TypeSystem.CodeGenerator CreateCodeGenerator (this Ide.Gui.Document doc) + { + return MonoDevelop.Ide.TypeSystem.CodeGenerator.CreateGenerator (doc); + } + + + // public static MonoDevelop.Ide.TypeSystem.CodeGenerator CreateCodeGenerator (this ITextDocument data, ICompilation compilation) + // { + // return MonoDevelop.Ide.TypeSystem.CodeGenerator.CreateGenerator (data, compilation); + // } + // + // static IUnresolvedTypeDefinition GetMainPart (IType t) + // { + // return t.GetDefinition ().Parts.First (); + // } + + + public static void AddAttribute (INamedTypeSymbol cls, string name, params object [] parameters) + { + if (cls == null) + throw new ArgumentNullException ("cls"); + bool isOpen; + string fileName = cls.Locations.First ().SourceTree.FilePath; + var buffer = TextFileProvider.Instance.GetTextEditorData (fileName, out isOpen); + + + var code = new StringBuilder (); + int pos = cls.Locations.First ().SourceSpan.Start; + var line = buffer.GetLineByOffset (pos); + code.Append (buffer.GetLineIndent (line)); + code.Append ("["); + code.Append (name); + if (parameters != null && parameters.Length > 0) { + code.Append ("("); + for (int i = 0; i < parameters.Length; i++) { + if (i > 0) + code.Append (", "); + code.Append (parameters [i]); + } + code.Append (")"); + } + code.Append ("]"); + code.AppendLine (); + + buffer.InsertText (line.Offset, code.ToString ()); + + if (!isOpen) { + File.WriteAllText (fileName, buffer.Text); + } + } + + public static ITypeSymbol AddType (DotNetProject project, string folder, string namspace, ClassDeclarationSyntax type) + { + if (project == null) + throw new ArgumentNullException (nameof (project)); + if (folder == null) + throw new ArgumentNullException (nameof (folder)); + if (namspace == null) + throw new ArgumentNullException (nameof (namspace)); + if (type == null) + throw new ArgumentNullException (nameof (type)); + var ns = SyntaxFactory.NamespaceDeclaration (SyntaxFactory.ParseName (namspace)).WithMembers (new SyntaxList<MemberDeclarationSyntax> () { type }); + + string fileName = project.LanguageBinding.GetFileName (Path.Combine (folder, type.Identifier.ToString ())); + using (var sw = new StreamWriter (fileName)) { + sw.WriteLine (ns.ToString ()); + } + FileService.NotifyFileChanged (fileName); + var roslynProject = TypeSystemService.GetCodeAnalysisProject (project); + var id = TypeSystemService.GetDocumentId (roslynProject.Id, fileName); + if (id == null) + return null; + var model = roslynProject.GetDocument (id).GetSemanticModelAsync ().Result; + var typeSyntax = model.SyntaxTree.GetCompilationUnitRoot ().ChildNodes ().First ().ChildNodes ().First () as ClassDeclarationSyntax; + return model.GetDeclaredSymbol (typeSyntax); + } + } +} |