diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/Designer/BooDesignerLoader.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/Designer/BooDesignerLoader.cs index e7ef25dbed..eea1159c6e 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/Designer/BooDesignerLoader.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/Designer/BooDesignerLoader.cs @@ -111,7 +111,7 @@ namespace Grunwald.BooBinding.Designer Module mainModule = Parse(textEditorControl.FileName, lastTextContent); IClass formClass; - List parts = NRefactoryDesignerLoader.FindFormClassParts(parseInfo, out formClass); + IList parts = NRefactoryDesignerLoader.FindFormClassParts(parseInfo, out formClass); IMethod initMethod = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(formClass); diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerLoader/NRefactoryDesignerLoader.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerLoader/NRefactoryDesignerLoader.cs index d696846108..f760762f28 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerLoader/NRefactoryDesignerLoader.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerLoader/NRefactoryDesignerLoader.cs @@ -127,7 +127,7 @@ namespace ICSharpCode.FormsDesigner string lastTextContent; - public static List FindFormClassParts(ParseInformation parseInfo, out IClass formClass) + public static IList FindFormClassParts(ParseInformation parseInfo, out IClass formClass) { #if DEBUG if ((Control.ModifierKeys & (Keys.Alt | Keys.Control)) == (Keys.Alt | Keys.Control)) { @@ -147,14 +147,11 @@ namespace ICSharpCode.FormsDesigner // Initialize designer for formClass formClass = formClass.GetCompoundClass(); - List parts; if (formClass is CompoundClass) { - parts = (formClass as CompoundClass).Parts; + return (formClass as CompoundClass).GetParts(); } else { - parts = new List(); - parts.Add(formClass); + return new IClass[] { formClass }; } - return parts; } // Steps to load the designer: @@ -173,7 +170,7 @@ namespace ICSharpCode.FormsDesigner ParseInformation parseInfo = ParserService.GetParseInformation(textEditorControl.FileName); IClass formClass; - List parts = FindFormClassParts(parseInfo, out formClass); + IList parts = FindFormClassParts(parseInfo, out formClass); List> compilationUnits = new List>(); bool foundInitMethod = false; diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs index 0a5679bae2..35e1b48069 100644 --- a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs +++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs @@ -236,7 +236,7 @@ namespace Hornung.ResourceToolkit.Resolver if (resourceClass != null) { CompoundClass cc = resourceClass.GetCompoundClass() as CompoundClass; - foreach (IClass c in (cc == null ? new IClass[] { resourceClass } : (IEnumerable)cc.Parts)) { + foreach (IClass c in (cc == null ? new IClass[] { resourceClass } : cc.GetParts())) { if (c.CompilationUnit != null && c.CompilationUnit.FileName != null) { #if DEBUG diff --git a/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ProjectNode.cs b/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ProjectNode.cs index 7da0b90a58..a5217a2ac0 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ProjectNode.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ProjectNode.cs @@ -66,7 +66,7 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser if (node != null) { CompoundClass cc = c as CompoundClass; - if (cc != null && cc.Parts.Count > 0) { + if (cc != null) { node.Class = cc; // update members after part has been removed } else { path.Nodes.Remove(node); diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs index d64c39b28f..2b4b91041f 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring { CompoundClass cc = c as CompoundClass; if (cc != null) { - return cc.Parts; + return cc.GetParts(); } else { return new IClass[] {c}; } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs index e3e7f71646..c75104e80b 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs @@ -314,7 +314,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring List list = new List(); CompoundClass cc = c as CompoundClass; if (cc != null) { - foreach (IClass part in cc.Parts) { + foreach (IClass part in cc.GetParts()) { string fileName = part.CompilationUnit.FileName; if (fileName != null) list.Add(fileName); diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/QuickClassBrowserPanel.cs b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/QuickClassBrowserPanel.cs index cce27838fe..a8445c3ea3 100644 --- a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/QuickClassBrowserPanel.cs +++ b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/QuickClassBrowserPanel.cs @@ -339,7 +339,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor IClass currentPart = c; if (c.IsPartial) { CompoundClass cc = c.GetCompoundClass() as CompoundClass; - if (cc != null && cc.Parts.Count > 0) { + if (cc != null) { partialMode = true; c = cc; } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractDecoration.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractDecoration.cs index d7045482e9..43771c6ed6 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractDecoration.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AbstractDecoration.cs @@ -170,11 +170,6 @@ namespace ICSharpCode.SharpDevelop.Dom return (modifiers & ModifierEnum.New) == ModifierEnum.New; } } - public bool IsPartial { - get { - return (modifiers & ModifierEnum.Partial) == ModifierEnum.Partial; - } - } public bool IsSynthetic { get { return (modifiers & ModifierEnum.Synthetic) == ModifierEnum.Synthetic; diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs index 34e71f15fc..a823ffd228 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/CompoundClass.cs @@ -15,14 +15,20 @@ namespace ICSharpCode.SharpDevelop.Dom /// public class CompoundClass : DefaultClass { - List parts = new List(); + /// + /// The parts this class is based on. + /// Requires manual locking! + /// + internal List parts = new List(); /// - /// Gets the parts this class is based on. + /// Gets the parts this class is based on. This method is thread-safe and + /// returns a copy of the list! /// - public List Parts { - get { - return parts; + public IList GetParts() + { + lock (this) { + return parts.ToArray(); } } @@ -38,7 +44,7 @@ namespace ICSharpCode.SharpDevelop.Dom /// /// Re-calculate information from class parts (Modifier, Base classes, Type parameters etc.) /// - public void UpdateInformationFromParts() + internal void UpdateInformationFromParts() { // Common for all parts: this.ClassType = parts[0].ClassType; @@ -47,8 +53,8 @@ namespace ICSharpCode.SharpDevelop.Dom ModifierEnum modifier = ModifierEnum.None; const ModifierEnum defaultClassVisibility = ModifierEnum.Internal; + this.BaseTypes.Clear(); - this.TypeParameters.Clear(); this.Attributes.Clear(); foreach (IClass part in parts) { if ((part.Modifiers & ModifierEnum.VisibilityMask) != defaultClassVisibility) { @@ -61,9 +67,6 @@ namespace ICSharpCode.SharpDevelop.Dom this.BaseTypes.Add(rt); } } - foreach (ITypeParameter typeParam in part.TypeParameters) { - this.TypeParameters.Add(typeParam); - } foreach (IAttribute attribute in part.Attributes) { this.Attributes.Add(attribute); } @@ -74,6 +77,23 @@ namespace ICSharpCode.SharpDevelop.Dom this.Modifiers = modifier; } + /// + /// Type parameters are same on all parts + /// + public override IList TypeParameters { + get { + lock (this) { + // Locking for the time of getting the reference to the sub-list is sufficient: + // Classes used for parts never change, instead the whole part is replaced with + // a new IClass instance. + return parts[0].TypeParameters; + } + } + set { + throw new NotSupportedException(); + } + } + /// /// CompoundClass has a normal return type even though IsPartial is set. /// @@ -84,51 +104,61 @@ namespace ICSharpCode.SharpDevelop.Dom public override List InnerClasses { get { - List l = new List(); - foreach (IClass part in parts) { - l.AddRange(part.InnerClasses); + lock (this) { + List l = new List(); + foreach (IClass part in parts) { + l.AddRange(part.InnerClasses); + } + return l; } - return l; } } public override List Fields { get { - List l = new List(); - foreach (IClass part in parts) { - l.AddRange(part.Fields); + lock (this) { + List l = new List(); + foreach (IClass part in parts) { + l.AddRange(part.Fields); + } + return l; } - return l; } } public override List Properties { get { - List l = new List(); - foreach (IClass part in parts) { - l.AddRange(part.Properties); + lock (this) { + List l = new List(); + foreach (IClass part in parts) { + l.AddRange(part.Properties); + } + return l; } - return l; } } public override List Methods { get { - List l = new List(); - foreach (IClass part in parts) { - l.AddRange(part.Methods); + lock (this) { + List l = new List(); + foreach (IClass part in parts) { + l.AddRange(part.Methods); + } + return l; } - return l; } } public override List Events { get { - List l = new List(); - foreach (IClass part in parts) { - l.AddRange(part.Events); + lock (this) { + List l = new List(); + foreach (IClass part in parts) { + l.AddRange(part.Events); + } + return l; } - return l; } } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs index 4470c2258e..4f9ee0d330 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs @@ -121,6 +121,19 @@ namespace ICSharpCode.SharpDevelop.Dom } } + public bool IsPartial { + get { + return (this.Modifiers & ModifierEnum.Partial) == ModifierEnum.Partial; + } + set { + if (value) + this.Modifiers |= ModifierEnum.Partial; + else + this.Modifiers &= ~ModifierEnum.Partial; + defaultReturnType = null; // re-create default return type + } + } + public IClass GetCompoundClass() { return this.DefaultReturnType.GetUnderlyingClass() ?? this; diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs index 9cd5185856..5207db6965 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IClass.cs @@ -131,5 +131,10 @@ namespace ICSharpCode.SharpDevelop.Dom bool HasExtensionMethods { get; } + + bool IsPartial { + get; + set; + } } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDecoration.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDecoration.cs index 71c71e7755..cd7388d06b 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDecoration.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/IDecoration.cs @@ -70,10 +70,6 @@ namespace ICSharpCode.SharpDevelop.Dom get; } - bool IsPartial { - get; - } - bool IsReadonly { get; } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs index a10805ffd1..685bd16cde 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs @@ -114,7 +114,7 @@ namespace ICSharpCode.SharpDevelop.Dom } /// - /// Gets if namespaces can be imported (i.e. Imports System, Dim a As Collections.ArrayList) + /// Gets if namespaces are imported (i.e. Imports System, Dim a As Collections.ArrayList) /// public virtual bool ImportNamespaces { get { @@ -140,6 +140,16 @@ namespace ICSharpCode.SharpDevelop.Dom } } + /// + /// Gets if the language allows partial classes where the partial modifier is not + /// used on any part. + /// + public virtual bool ImplicitPartialClasses { + get { + return false; + } + } + /// /// Allow invoking an object constructor outside of ExpressionContext.ObjectCreation. /// Used for Boo, which creates instances like this: 'self.Size = Size(10, 20)' diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs index eb3a166479..1ca11582f2 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs @@ -305,29 +305,44 @@ namespace ICSharpCode.SharpDevelop.Dom protected void AddClassToNamespaceListInternal(IClass addClass) { string fullyQualifiedName = addClass.FullyQualifiedName; - if (addClass.IsPartial) { - LoggingService.Debug("Adding partial class " + addClass.Name + " from " + Path.GetFileName(addClass.CompilationUnit.FileName)); - CompoundClass compound = GetClassInternal(fullyQualifiedName, addClass.TypeParameters.Count, language) as CompoundClass; + IClass existingClass = GetClassInternal(fullyQualifiedName, addClass.TypeParameters.Count, language); + if (existingClass != null && existingClass.TypeParameters.Count == addClass.TypeParameters.Count) { + //LoggingService.Debug("Adding partial class " + addClass.Name + " from " + Path.GetFileName(addClass.CompilationUnit.FileName)); + CompoundClass compound = existingClass as CompoundClass; if (compound != null) { + // mark the class as partial + // (VB allows specifying the 'partial' modifier only on one part) + addClass.IsPartial = true; + // possibly replace existing class (look for CU with same filename) lock (compound) { - for (int i = 0; i < compound.Parts.Count; i++) { - if (compound.Parts[i].CompilationUnit.FileName == addClass.CompilationUnit.FileName) { - compound.Parts[i] = addClass; + for (int i = 0; i < compound.parts.Count; i++) { + if (compound.parts[i].CompilationUnit.FileName == addClass.CompilationUnit.FileName) { + compound.parts[i] = addClass; compound.UpdateInformationFromParts(); - LoggingService.Debug("Replaced old part!"); + //LoggingService.Debug("Replaced old part!"); return; } } - compound.Parts.Add(addClass); + compound.parts.Add(addClass); compound.UpdateInformationFromParts(); } - LoggingService.Debug("Added new part!"); + //LoggingService.Debug("Added new part!"); return; - } else { - addClass = new CompoundClass(addClass); - LoggingService.Debug("Compound created!"); + } else if (addClass.IsPartial || language.ImplicitPartialClasses) { + // Merge existing non-partial class with addClass + + // Ensure partial modifier is set everywhere: + addClass.IsPartial = true; + existingClass.IsPartial = true; + + addClass = compound = new CompoundClass(addClass); + compound.parts.Add(existingClass); + compound.UpdateInformationFromParts(); } + } else if (addClass.IsPartial) { + addClass = new CompoundClass(addClass); + //LoggingService.Debug("Compound created!"); } IClass oldDictionaryClass; @@ -471,8 +486,8 @@ namespace ICSharpCode.SharpDevelop.Dom CompoundClass compound = GetClassInternal(fullyQualifiedName, @class.TypeParameters.Count, language) as CompoundClass; if (compound == null) return; lock (compound) { - compound.Parts.Remove(@class); - if (compound.Parts.Count > 0) { + compound.parts.Remove(@class); + if (compound.parts.Count > 0) { compound.UpdateInformationFromParts(); return; } else {