From 16313e486b855fc46f88e32d1a1a7a96a4b6f068 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Jun 2013 20:57:18 +0200 Subject: [PATCH] Add CSharpDesignerGenerator. --- .../Project/CSharpBinding.csproj | 1 + .../Project/Src/ExtensionMethods.cs | 18 - .../FormsDesigner/CSharpDesignerGenerator.cs | 329 ++++++++++++++++++ .../Src/FormsDesigner/CSharpDesignerLoader.cs | 40 ++- .../CSharpFormsDesignerLoaderContext.cs | 12 +- .../ICSharpDesignerLoaderContext.cs | 5 +- .../FormsDesigner/SecondaryDisplayBinding.cs | 16 +- .../Src/Refactoring/RefactoringExtensions.cs | 22 ++ .../Src/Refactoring/SDRefactoringContext.cs | 2 +- .../Project/Src/DesignerSourceCodeStorage.cs | 15 +- .../Project/Src/XmlFormattingStrategy.cs | 2 +- .../Base/Project/Editor/DocumentUtilities.cs | 5 + .../Src/Editor/Commands/PasteAsCommands.cs | 9 +- .../Workbench/WorkbenchStartup.cs | 7 + 14 files changed, 424 insertions(+), 59 deletions(-) create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerGenerator.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 413773e546..cb3de8ebaf 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -78,6 +78,7 @@ + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ExtensionMethods.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ExtensionMethods.cs index c43732370f..2a0aea4ea6 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ExtensionMethods.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ExtensionMethods.cs @@ -54,23 +54,5 @@ namespace CSharpBinding return new CSharpAstResolver(compilation, new SyntaxTree(), new CSharpUnresolvedFile { FileName = ec.FileName }); }); } - - /// - /// Retrieves the declaration for the specified entity. - /// Returns null if the entity is not defined in C# source code. - /// - public static EntityDeclaration GetDeclaration(this IEntity entity, out CSharpFullParseInformation parseInfo) - { - if (entity == null || string.IsNullOrEmpty(entity.Region.FileName)) { - parseInfo = null; - return null; - } - parseInfo = SD.ParserService.Parse(FileName.Create(entity.Region.FileName), - parentProject: entity.ParentAssembly.GetProject()) - as CSharpFullParseInformation; - if (parseInfo == null) - return null; - return parseInfo.SyntaxTree.GetNodeAt(entity.Region.Begin); - } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerGenerator.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerGenerator.cs new file mode 100644 index 0000000000..fd5b2e52a5 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerGenerator.cs @@ -0,0 +1,329 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using ICSharpCode.Core; +using ICSharpCode.FormsDesigner; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Project; +using Microsoft.CSharp; +using CSharpBinding.Parser; +using CSharpBinding.Refactoring; + +namespace CSharpBinding.FormsDesigner +{ + public class CSharpDesignerGenerator + { + readonly CSharpFullParseInformation primaryParseInfo; + readonly ICSharpDesignerLoaderContext context; + readonly ICompilation compilation; + readonly IUnresolvedTypeDefinition primaryPart; + readonly ITypeDefinition formClass; + readonly IMethod initializeComponents; + + public CSharpDesignerGenerator(ICSharpDesignerLoaderContext context) + { + this.context = context; + this.primaryParseInfo = context.GetPrimaryFileParseInformation(); + this.compilation = context.GetCompilation(); + + // Find designer class + formClass = FormsDesignerSecondaryDisplayBinding.GetDesignableClass(primaryParseInfo.UnresolvedFile, compilation, out primaryPart); + initializeComponents = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(formClass); + if (initializeComponents == null) + throw new FormsDesignerLoadException("Could not find InitializeComponents"); + } + + public void MergeFormChanges(CodeCompileUnit codeUnit) + { + var codeNamespace = codeUnit.Namespaces.Cast().Single(); + var codeClass = codeNamespace.Types.Cast().Single(); + var codeMethod = codeClass.Members.OfType().Single(m => m.Name == "InitializeComponent"); + var codeFields = codeClass.Members.OfType().ToList(); + RemoveUnsupportedCode(codeClass, codeMethod); + + SaveInitializeComponents(codeMethod); + MergeFields(codeFields); + ApplyScripts(); + } + + #region RemoveUnsupportedCode + /// + /// This method solves all problems that are caused if the designer generates + /// code that is not supported in a previous version of .NET. (3.5 and below) + /// Currently it fixes: + /// - remove calls to ISupportInitialize.BeginInit/EndInit, if the interface is not implemented by the type in the target framework. + /// + /// When adding new workarounds make sure that the code does not remove too much code! + void RemoveUnsupportedCode(CodeTypeDeclaration codeClass, CodeMemberMethod initializeComponent) + { + if (compilation.GetProject() is MSBuildBasedProject) { + MSBuildBasedProject p = (MSBuildBasedProject)compilation.GetProject(); + string v = (p.GetEvaluatedProperty("TargetFrameworkVersion") ?? "").Trim('v'); + Version version; + if (!Version.TryParse(v, out version) || version.Major >= 4) + return; + } + + List stmtsToRemove = new List(); + var iSupportInitializeInterface = compilation.FindType(typeof(ISupportInitialize)).GetDefinition(); + + if (iSupportInitializeInterface == null) + return; + + foreach (var stmt in initializeComponent.Statements.OfType().Where(ces => ces.Expression is CodeMethodInvokeExpression)) { + CodeMethodInvokeExpression invocation = (CodeMethodInvokeExpression)stmt.Expression; + CodeCastExpression expr = invocation.Method.TargetObject as CodeCastExpression; + if (expr != null) { + if (expr.TargetType.BaseType != "System.ComponentModel.ISupportInitialize") + continue; + var fieldType = GetTypeOfControl(expr.Expression, initializeComponent, codeClass).GetDefinition(); + if (fieldType == null) + continue; + if (!fieldType.IsDerivedFrom(iSupportInitializeInterface)) + stmtsToRemove.Add(stmt); + } + } + + foreach (var stmt in stmtsToRemove) { + initializeComponent.Statements.Remove(stmt); + } + } + + /// + /// Tries to find the type of the expression. + /// + IType GetTypeOfControl(CodeExpression expression, CodeMemberMethod initializeComponentMethod, CodeTypeDeclaration formTypeDeclaration) + { + if (expression is CodeVariableReferenceExpression) { + string name = ((CodeVariableReferenceExpression)expression).VariableName; + var decl = initializeComponentMethod.Statements.OfType().Single(v => v.Name == name); + return ReflectionHelper.ParseReflectionName(decl.Type.BaseType).Resolve(compilation); + } + if (expression is CodeFieldReferenceExpression && ((CodeFieldReferenceExpression)expression).TargetObject is CodeThisReferenceExpression) { + string name = ((CodeFieldReferenceExpression)expression).FieldName; + var decl = formTypeDeclaration.Members.OfType().FirstOrDefault(f => name == f.Name); + if (decl != null) + return ReflectionHelper.ParseReflectionName(decl.Type.BaseType).Resolve(compilation); + var field = formClass.GetFields(f => f.Name == name).LastOrDefault(); + if (field == null) + return SpecialType.UnknownType; + return field.Type; + } + return SpecialType.UnknownType; + } + #endregion + + #region Script management + Dictionary scripts = new Dictionary(); + + DocumentScript GetScript(string fileName) + { + DocumentScript script; + var fileNameObj = FileName.Create(fileName); + if (scripts.TryGetValue(fileNameObj, out script)) + return script; + + IDocument document = context.GetDocument(fileNameObj); + var ctx = SDRefactoringContext.Create(fileNameObj, document); + script = new DocumentScript(document, FormattingOptionsFactory.CreateSharpDevelop(), new TextEditorOptions()); + scripts.Add(fileNameObj, script); + return script; + } + + void ApplyScripts() + { + foreach (var pair in scripts) { + var script = pair.Value; + IDocument newDocument = script.CurrentDocument; + script.Dispose(); + SD.ParserService.ParseFileAsync(pair.Key, newDocument).FireAndForget(); + Debug.Assert(FileName.Create(newDocument.FileName) == pair.Key); + } + scripts.Clear(); + } + #endregion + + #region SaveInitializeComponents + void SaveInitializeComponents(CodeMemberMethod codeMethod) + { + var bodyRegion = initializeComponents.BodyRegion; + DocumentScript script = GetScript(bodyRegion.FileName); + + string newline = DocumentUtilities.GetLineTerminator(script.OriginalDocument, bodyRegion.BeginLine); + string indentation = DocumentUtilities.GetIndentation(script.OriginalDocument, bodyRegion.BeginLine); + string code = "{" + newline + GenerateInitializeComponents(codeMethod, indentation, newline) + indentation + "}"; + + int startOffset = script.GetCurrentOffset(bodyRegion.Begin); + int endOffset = script.GetCurrentOffset(bodyRegion.End); + script.Replace(startOffset, endOffset - startOffset, code); + } + + string GenerateInitializeComponents(CodeMemberMethod codeMethod, string indentation, string newline) + { + var writer = new StringWriter(); + writer.NewLine = newline; + var options = new CodeGeneratorOptions(); + options.IndentString = SD.EditorControlService.GlobalOptions.IndentationString; + var codeProvider = new CSharpCodeProvider(); + foreach (CodeStatement statement in codeMethod.Statements) { + writer.Write(indentation); + // indentation isn't generated when calling GenerateCodeFromStatement + writer.Write(options.IndentString); + try { + codeProvider.GenerateCodeFromStatement(statement, writer, options); + } catch (Exception e) { + writer.WriteLine("// TODO: Error while generating statement : " + e.Message); + SD.Log.Error(e); + } + } + + return writer.ToString(); + } + #endregion + + #region MergeFields + void MergeFields(List codeFields) + { + // apply changes the designer made to field declarations + // first loop looks for added and changed fields + foreach (CodeMemberField newField in codeFields) { + IField oldField = formClass.Fields.FirstOrDefault(f => f.Name == newField.Name); + if (oldField == null) { + CreateField(newField); + } else if (FieldChanged(oldField, newField)) { + UpdateField(oldField, newField); + } + } + + // second loop looks for removed fields + foreach (IField field in formClass.Fields) { + if (!codeFields.Any(f => f.Name == field.Name)) { + RemoveField(field); + } + } + } + + /// + /// Compares the SharpDevelop.Dom field declaration oldField to + /// the CodeDom field declaration newField. + /// + /// true, if the fields are different in type or modifiers, otherwise false. + static bool FieldChanged(IField oldField, CodeMemberField newField) + { + // compare types + if (AreTypesDifferent(oldField.ReturnType, newField.Type)) { + SD.Log.Debug("FieldChanged (type): "+oldField.Name+", "+oldField.ReturnType.FullName+" -> "+newField.Type.BaseType); + return true; + } + + // compare accessibility modifiers + Accessibility oldModifiers = oldField.Accessibility; + MemberAttributes newModifiers = newField.Attributes & MemberAttributes.AccessMask; + + // SharpDevelop.Dom always adds Private modifier, even if not specified + // CodeDom omits Private modifier if not present (although it is the default) + if (oldModifiers == Accessibility.Private) { + if (newModifiers != 0 && newModifiers != MemberAttributes.Private) { + return true; + } + } + + Accessibility[] sdModifiers = {Accessibility.Protected, Accessibility.ProtectedAndInternal, Accessibility.Internal, Accessibility.Public}; + MemberAttributes[] cdModifiers = {MemberAttributes.Family, MemberAttributes.FamilyOrAssembly, MemberAttributes.Assembly, MemberAttributes.Public}; + for (int i = 0; i < sdModifiers.Length; i++) { + if ((oldModifiers == sdModifiers[i]) ^ (newModifiers == cdModifiers[i])) { + return true; + } + } + + return false; + } + + static bool AreTypesDifferent(IType oldType, CodeTypeReference newType) + { + IType oldClass = oldType.GetDefinition(); + if (oldClass == null) { + // ignore type changes to fields with unresolved type + return false; + } + if (newType == null || newType.BaseType == "System.Void") { + // field types get replaced with System.Void if the type cannot be resolved + // (e.g. generic fields in the Boo designer which aren't converted to CodeDom) + // we'll ignore such type changes (fields should never have the type void) + return false; + } + + return oldType.ReflectionName != newType.BaseType; + } + + string GenerateField(CodeMemberField newField) + { + StringWriter writer = new StringWriter(); + var provider = new CSharpCodeProvider(); + provider.GenerateCodeFromMember(newField, writer, new CodeGeneratorOptions()); + return writer.ToString().Trim(); + } + + void CreateField(CodeMemberField newField) + { + // insert new field below InitializeComponents() + + var bodyRegion = initializeComponents.BodyRegion; + DocumentScript script = GetScript(bodyRegion.FileName); + string newline = DocumentUtilities.GetLineTerminator(script.OriginalDocument, bodyRegion.BeginLine); + string indentation = DocumentUtilities.GetIndentation(script.OriginalDocument, bodyRegion.BeginLine); + + var insertionLocation = new TextLocation(bodyRegion.EndLine + 1, 1); + int insertionOffset = script.GetCurrentOffset(insertionLocation); + string code = indentation + GenerateField(newField) + newline; + script.InsertText(insertionOffset, code); + } + + void UpdateField(IField oldField, CodeMemberField newField) + { + DomRegion region = oldField.Region; + DocumentScript script = GetScript(region.FileName); + + int offset = script.GetCurrentOffset(region.Begin); + int endOffset = script.GetCurrentOffset(region.End); + string code = GenerateField(newField); + script.Replace(offset, endOffset - offset, code); + } + + void RemoveField(IField field) + { + DomRegion region = field.Region; + DocumentScript script = GetScript(region.FileName); + int offset = script.GetCurrentOffset(region.Begin); + int endOffset = script.GetCurrentOffset(region.End); + IDocumentLine line = script.CurrentDocument.GetLineByOffset(endOffset); + if (endOffset == line.EndOffset) { + endOffset += line.DelimiterLength; // delete the whole line + // delete indentation in front of the line + while (offset > 0 && IsTabOrSpace(script.CurrentDocument.GetCharAt(offset - 1))) + offset--; + } + script.RemoveText(offset, endOffset - offset); + } + + static bool IsTabOrSpace(char c) + { + return c == '\t' || c == ' '; + } + #endregion + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerLoader.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerLoader.cs index 5f660efec4..a570709dc8 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerLoader.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpDesignerLoader.cs @@ -16,6 +16,7 @@ using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop; using Microsoft.CSharp; using CSharpBinding.Parser; +using CSharpBinding.Refactoring; namespace CSharpBinding.FormsDesigner { @@ -29,11 +30,6 @@ namespace CSharpBinding.FormsDesigner this.context = context; } - protected override void Write(CodeCompileUnit unit) - { - throw new NotImplementedException(); - } - protected override CodeDomProvider CodeDomProvider { get { return codeDomProvider; @@ -47,8 +43,6 @@ namespace CSharpBinding.FormsDesigner return base.IsReloadNeeded() || context.DesignerCodeFileDocument.Version.Equals(lastTextContentVersion); } - IUnresolvedTypeDefinition primaryPart; - // Steps to load the designer: // - Parse main file // - Find other files containing parts of the form @@ -65,17 +59,9 @@ namespace CSharpBinding.FormsDesigner var compilation = context.GetCompilation(); // Find designer class - ITypeDefinition designerClass = null; - IMethod initializeComponents = null; - foreach (var utd in primaryParseInfo.UnresolvedFile.TopLevelTypeDefinitions) { - var td = utd.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); - if (td != null && FormsDesignerSecondaryDisplayBinding.IsDesignable(td)) { - primaryPart = utd; - designerClass = td; - initializeComponents = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(td); - break; - } - } + IUnresolvedTypeDefinition primaryPart; + ITypeDefinition designerClass = FormsDesignerSecondaryDisplayBinding.GetDesignableClass(primaryParseInfo.UnresolvedFile, compilation, out primaryPart); + IMethod initializeComponents = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(designerClass); if (initializeComponents == null) { throw new FormsDesignerLoadException("The InitializeComponent method was not found. Designer cannot be loaded."); @@ -135,5 +121,23 @@ namespace CSharpBinding.FormsDesigner return codeUnit; } + + protected override void Write(CodeCompileUnit unit) + { + LoggingService.Info("DesignerLoader.Write called"); + // output generated CodeDOM to the console : + #if DEBUG + if ((Control.ModifierKeys & Keys.Control) == Keys.Control) { + this.CodeDomProvider.GenerateCodeFromCompileUnit(unit, Console.Out, null); + } + #endif + try { + var generator = new CSharpDesignerGenerator(context); + generator.MergeFormChanges(unit); + } catch (Exception ex) { + SD.AnalyticsMonitor.TrackException(ex); + MessageService.ShowException(ex); + } + } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpFormsDesignerLoaderContext.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpFormsDesignerLoaderContext.cs index f09f4c56c9..6c96e32500 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpFormsDesignerLoaderContext.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/CSharpFormsDesignerLoaderContext.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using ICSharpCode.Core; using ICSharpCode.FormsDesigner; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; @@ -13,7 +14,7 @@ namespace CSharpBinding.FormsDesigner class CSharpFormsDesignerLoaderContext : ICSharpDesignerLoaderContext { readonly FormsDesignerViewContent viewContent; - + public CSharpFormsDesignerLoaderContext(FormsDesignerViewContent viewContent) { this.viewContent = viewContent; @@ -41,6 +42,15 @@ namespace CSharpBinding.FormsDesigner { return SD.ParserService.GetCompilationForFile(viewContent.PrimaryFileName); } + + public IDocument GetDocument(FileName fileName) + { + foreach (var pair in viewContent.SourceFiles) { + if (pair.Key.FileName == fileName) + return pair.Value; + } + throw new InvalidOperationException("Designer file not found"); + } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/ICSharpDesignerLoaderContext.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/ICSharpDesignerLoaderContext.cs index e92fd99d44..930c264fd3 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/ICSharpDesignerLoaderContext.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/ICSharpDesignerLoaderContext.cs @@ -2,20 +2,19 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using ICSharpCode.Core; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; using CSharpBinding.Parser; namespace CSharpBinding.FormsDesigner { - /// - /// Description of ICSharpDesignerLoaderContext. - /// public interface ICSharpDesignerLoaderContext { //IDocument PrimaryFileDocument { get; } IDocument DesignerCodeFileDocument { get; } CSharpFullParseInformation GetPrimaryFileParseInformation(); ICompilation GetCompilation(); + IDocument GetDocument(FileName fileName); } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/SecondaryDisplayBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/SecondaryDisplayBinding.cs index 423cd1fdee..5f827ea68a 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/SecondaryDisplayBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormsDesigner/SecondaryDisplayBinding.cs @@ -31,6 +31,8 @@ namespace CSharpBinding.FormsDesigner public static IMethod GetInitializeComponents(ITypeDefinition c) { + if (c == null) + return null; foreach (IMethod method in c.Methods) { if (IsInitializeComponentsMethodName(method.Name) && method.Parameters.Count == 0) { return method; @@ -62,15 +64,23 @@ namespace CSharpBinding.FormsDesigner public static bool IsDesignable(IUnresolvedFile parsedFile, ICompilation compilation) { + IUnresolvedTypeDefinition td; + return GetDesignableClass(parsedFile, compilation, out td) != null; + } + + public static ITypeDefinition GetDesignableClass(IUnresolvedFile parsedFile, ICompilation compilation, out IUnresolvedTypeDefinition primaryPart) + { + primaryPart = null; if (parsedFile == null) - return false; + return null; foreach (var utd in parsedFile.TopLevelTypeDefinitions) { var td = utd.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); if (IsDesignable(td)) { - return true; + primaryPart = utd; + return td; } } - return false; + return null; } public bool CanAttachTo(IViewContent viewContent) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/RefactoringExtensions.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/RefactoringExtensions.cs index b24086bb6a..0fba0bf336 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/RefactoringExtensions.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/RefactoringExtensions.cs @@ -2,7 +2,11 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using ICSharpCode.Core; +using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; +using CSharpBinding.Parser; namespace CSharpBinding.Refactoring { @@ -26,5 +30,23 @@ namespace CSharpBinding.Refactoring return property.CanGet && !property.Getter.HasBody && property.CanSet && !property.Setter.HasBody; } + + /// + /// Retrieves the declaration for the specified entity. + /// Returns null if the entity is not defined in C# source code. + /// + public static EntityDeclaration GetDeclaration(this IEntity entity, out CSharpFullParseInformation parseInfo) + { + if (entity == null || string.IsNullOrEmpty(entity.Region.FileName)) { + parseInfo = null; + return null; + } + parseInfo = SD.ParserService.Parse(FileName.Create(entity.Region.FileName), + parentProject: entity.ParentAssembly.GetProject()) + as CSharpFullParseInformation; + if (parseInfo == null) + return null; + return parseInfo.SyntaxTree.GetNodeAt(entity.Region.Begin); + } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDRefactoringContext.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDRefactoringContext.cs index a5d807bec4..2f097687b5 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDRefactoringContext.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDRefactoringContext.cs @@ -36,7 +36,7 @@ namespace CSharpBinding.Refactoring return Create(editor.FileName, editor.Document, editor.Caret.Location, cancellationToken); } - public static SDRefactoringContext Create(FileName fileName, ITextSource textSource, TextLocation location, CancellationToken cancellationToken) + public static SDRefactoringContext Create(FileName fileName, ITextSource textSource, TextLocation location = default(TextLocation), CancellationToken cancellationToken = default(CancellationToken)) { var parseInfo = SD.ParserService.Parse(fileName, textSource, cancellationToken: cancellationToken) as CSharpFullParseInformation; var compilation = SD.ParserService.GetCompilationForFile(fileName); diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs index faa7e4f480..8e7cdfd597 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Text; using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.Core; using ICSharpCode.NRefactory.Editor; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; @@ -73,7 +74,7 @@ namespace ICSharpCode.FormsDesigner FileContent c; if (!this.fileContents.TryGetValue(file, out c)) { - c = new FileContent(); + c = new FileContent(file.FileName); this.fileContents.Add(file, c); } c.LoadFrom(stream); @@ -98,7 +99,7 @@ namespace ICSharpCode.FormsDesigner /// public void AddFile(OpenedFile file) { - this.fileContents.Add(file, new FileContent()); + this.fileContents.Add(file, new FileContent(file.FileName)); } /// @@ -106,7 +107,7 @@ namespace ICSharpCode.FormsDesigner /// public void AddFile(OpenedFile file, Encoding encoding) { - this.fileContents.Add(file, new FileContent(encoding)); + this.fileContents.Add(file, new FileContent(file.FileName, encoding)); } /// @@ -159,13 +160,13 @@ namespace ICSharpCode.FormsDesigner readonly IDocument document; readonly bool doNotLoad; - public FileContent() - : this(SD.FileService.DefaultFileEncoding) + public FileContent(FileName fileName) + : this(fileName, SD.FileService.DefaultFileEncoding) { } - public FileContent(Encoding encoding) - : this(new TextDocument(), encoding) + public FileContent(FileName fileName, Encoding encoding) + : this(new TextDocument { FileName = fileName }, encoding) { } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlFormattingStrategy.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlFormattingStrategy.cs index b991d0c966..22cf9c87bb 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlFormattingStrategy.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlFormattingStrategy.cs @@ -161,7 +161,7 @@ namespace ICSharpCode.XmlEditor if (r.NodeType == XmlNodeType.Element) { tagStack.Push(currentIndentation); if (r.LineNumber < begin) - currentIndentation = DocumentUtilities.GetWhitespaceAfter(editor.Document, editor.Document.PositionToOffset(r.LineNumber, 1)); + currentIndentation = DocumentUtilities.GetIndentation(editor.Document, r.LineNumber); if (r.Name.Length < 16) attribIndent = currentIndentation + new string(' ', 2 + r.Name.Length); else diff --git a/src/Main/Base/Project/Editor/DocumentUtilities.cs b/src/Main/Base/Project/Editor/DocumentUtilities.cs index f043823bda..818c9b22ea 100644 --- a/src/Main/Base/Project/Editor/DocumentUtilities.cs +++ b/src/Main/Base/Project/Editor/DocumentUtilities.cs @@ -146,6 +146,11 @@ namespace ICSharpCode.SharpDevelop.Editor return char.IsLetterOrDigit(ch) || ch == '_'; } + public static string GetIndentation(IDocument document, int line) + { + return DocumentUtilities.GetWhitespaceAfter(document, document.GetLineByNumber(line).Offset); + } + /// /// Gets all indentation starting at offset. /// diff --git a/src/Main/Base/Project/Src/Editor/Commands/PasteAsCommands.cs b/src/Main/Base/Project/Src/Editor/Commands/PasteAsCommands.cs index f910de89f1..db8521b673 100644 --- a/src/Main/Base/Project/Src/Editor/Commands/PasteAsCommands.cs +++ b/src/Main/Base/Project/Src/Editor/Commands/PasteAsCommands.cs @@ -31,11 +31,6 @@ namespace ICSharpCode.SharpDevelop.Editor.Commands } protected abstract void Run(ITextEditor editor, string clipboardText); - - protected string GetIndentation(IDocument document, int line) - { - return DocumentUtilities.GetWhitespaceAfter(document, document.GetLineByNumber(line).Offset); - } } /// @@ -52,7 +47,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Commands { protected override void Run(ITextEditor editor, string clipboardText) { - string indentation = GetIndentation(editor.Document, editor.Caret.Line); + string indentation = DocumentUtilities.GetIndentation(editor.Document, editor.Caret.Line); IAmbience ambience = AmbienceService.GetCurrentAmbience(); int maxLineLength = editor.Options.VerticalRulerColumn - VisualIndentationLength(editor, indentation); StringWriter insertedText = new StringWriter(); @@ -154,7 +149,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Commands } if (expression == null) return; - string indentation = GetIndentation(editor.Document, editor.Caret.Line); + string indentation = DocumentUtilities.GetIndentation(editor.Document, editor.Caret.Line); CodeGeneratorOptions options = new CodeGeneratorOptions(); options.IndentString = editor.Options.IndentationString; StringWriter writer = new StringWriter(); diff --git a/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs b/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs index 02e18f49c1..797cd92585 100644 --- a/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs +++ b/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs @@ -11,6 +11,7 @@ using System.Windows.Interop; using System.Windows.Threading; using ICSharpCode.Core; +using ICSharpCode.Core.WinForms; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Project; @@ -52,6 +53,12 @@ namespace ICSharpCode.SharpDevelop.Workbench workbench.SetMemento(PropertyService.NestedProperties(workbenchMemento)); workbench.WorkbenchLayout = layout; + var dlgMsgService = SD.MessageService as IDialogMessageService; + if (dlgMsgService != null) { + dlgMsgService.DialogSynchronizeInvoke = SD.MainThread.SynchronizingObject; + dlgMsgService.DialogOwner = workbench.MainWin32Window; + } + var applicationStateInfoService = SD.GetService(); if (applicationStateInfoService != null) { applicationStateInfoService.RegisterStateGetter(activeContentState, delegate { return SD.Workbench.ActiveContent; });