Browse Source

Add support for updating the type definition model.

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
1c4923b873
  1. 5
      src/Main/Base/Project/Dom/IEntityModel.cs
  2. 9
      src/Main/Base/Project/Dom/IMemberModel.cs
  3. 10
      src/Main/Base/Project/Dom/ITypeDefinitionModel.cs
  4. 137
      src/Main/Base/Test/Dom/CSharpModelTests.cs
  5. 6
      src/Main/SharpDevelop/Dom/ModelFactory.cs
  6. 72
      src/Main/SharpDevelop/Dom/TypeDefinitionModel.cs

5
src/Main/Base/Project/Dom/IEntityModel.cs

@ -14,6 +14,11 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -14,6 +14,11 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </summary>
public interface IEntityModel : INotifyPropertyChanged
{
/// <summary>
/// Gets the name of the entity.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the parent project that contains this entity.
/// May return null if the entity is not part of a project.

9
src/Main/Base/Project/Dom/IMemberModel.cs

@ -16,12 +16,17 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -16,12 +16,17 @@ namespace ICSharpCode.SharpDevelop.Dom
/// Resolves the member in the current solution snapshot.
/// Returns null if the member could not be resolved.
/// </summary>
IMember Resolve();
new IMember Resolve();
/// <summary>
/// Resolves the member in the specified solution snapshot.
/// Returns null if the member could not be resolved.
/// </summary>
IMember Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot);
new IMember Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot);
/// <summary>
/// Updates the member model with the specified new member.
/// </summary>
void Update(IUnresolvedMember newMember);
}
}

10
src/Main/Base/Project/Dom/ITypeDefinitionModel.cs

@ -29,8 +29,14 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -29,8 +29,14 @@ namespace ICSharpCode.SharpDevelop.Dom
new ITypeDefinition Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot);
/// <summary>
/// Retrieves
/// Retrieves the nested type with the specified name and additional type parameter count
/// </summary>
ITypeDefinitionModel GetNestedType(string name, int atpc);
ITypeDefinitionModel GetNestedType(string name, int additionalTypeParameterCount);
/// <summary>
/// Updates this type definition model by replacing oldPart with newPart.
/// Either oldPart or newPart may be null when adding/removed a part.
/// </summary>
void Update(IUnresolvedTypeDefinition oldPart, IUnresolvedTypeDefinition newPart);
}
}

137
src/Main/Base/Test/Dom/CSharpModelTests.cs

@ -2,6 +2,10 @@ @@ -2,6 +2,10 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
@ -18,6 +22,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -18,6 +22,7 @@ namespace ICSharpCode.SharpDevelop.Dom
IProjectContent projectContent;
IEntityModelContext context;
TopLevelTypeDefinitionModelCollection topLevelTypeModels;
List<NotifyCollectionChangedEventArgs> topLevelChangeEventArgs = new List<NotifyCollectionChangedEventArgs>();
#region SetUp and other helper methods
[SetUp]
@ -25,12 +30,16 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -25,12 +30,16 @@ namespace ICSharpCode.SharpDevelop.Dom
{
SD.InitializeForUnitTests();
SD.Services.AddStrictMockService<IParserService>();
SD.Services.AddStrictMockService<IMessageLoop>();
SD.MainThread.Stub(m => m.VerifyAccess());
project = MockRepository.GenerateStrictMock<IProject>();
projectContent = new CSharpProjectContent().AddAssemblyReferences(AssemblyLoader.Corlib);
context = new ProjectEntityModelContext(project, ".cs");
topLevelTypeModels = new TopLevelTypeDefinitionModelCollection(context);
SD.ParserService.Stub(p => p.GetCompilation(project)).WhenCalled(c => c.ReturnValue = projectContent.CreateCompilation());
topLevelChangeEventArgs.Clear();
topLevelTypeModels.CollectionChanged += (sender, e) => topLevelChangeEventArgs.Add(e);
}
[TearDown]
@ -77,14 +86,140 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -77,14 +86,140 @@ namespace ICSharpCode.SharpDevelop.Dom
public void EmptyProject()
{
Assert.AreEqual(0, topLevelTypeModels.Count);
Assert.IsNull(topLevelTypeModels[new TopLevelTypeName("A")]);
Assert.IsNull(topLevelTypeModels[new TopLevelTypeName("MissingClass")]);
}
#region Simple class
[Test]
public void AddSimpleClass()
{
AddCodeFile("test.cs", @"class SimpleClass {}");
Assert.AreEqual(1, topLevelTypeModels.Count);
var simpleClass = topLevelTypeModels.Single();
Assert.AreEqual(NotifyCollectionChangedAction.Add, topLevelChangeEventArgs.Single().Action);
Assert.AreEqual(new[] { simpleClass }, topLevelChangeEventArgs.Single().NewItems);
topLevelChangeEventArgs.Clear(); // clear for follow-up tests
}
[Test]
public void UpdateSimpleClass()
{
AddSimpleClass();
var simpleClass = topLevelTypeModels.Single();
Assert.AreEqual(0, simpleClass.Members.Count);
UpdateCodeFile("test.cs", "class SimpleClass { void Method() {} }");
Assert.AreSame(simpleClass, topLevelTypeModels.Single());
Assert.AreEqual(1, simpleClass.Members.Count);
Assert.IsEmpty(topLevelChangeEventArgs);
}
[Test]
public void ReplaceSimpleClass()
{
AddSimpleClass();
var simpleClass = topLevelTypeModels.Single();
UpdateCodeFile("test.cs", "class OtherClass { }");
var otherClass = topLevelTypeModels.Single();
Assert.AreNotSame(simpleClass, otherClass);
Assert.AreEqual(NotifyCollectionChangedAction.Replace, topLevelChangeEventArgs.Single().Action);
Assert.AreEqual(new[] { simpleClass }, topLevelChangeEventArgs.Single().OldItems);
Assert.AreEqual(new[] { otherClass }, topLevelChangeEventArgs.Single().NewItems);
}
[Test]
public void RemoveSimpleClassCodeFile()
{
AddSimpleClass();
var simpleClass = topLevelTypeModels.Single();
RemoveCodeFile("test.cs");
Assert.IsEmpty(topLevelTypeModels);
// check removal event
Assert.AreEqual(NotifyCollectionChangedAction.Remove, topLevelChangeEventArgs.Single().Action);
Assert.AreEqual(new[] { simpleClass }, topLevelChangeEventArgs.Single().OldItems);
// test that accessing properties of a removed class still works
Assert.AreEqual(new FullTypeName("SimpleClass"), simpleClass.FullTypeName);
}
#endregion
#region TwoPartsInSingleFile
[Test]
public void TwoPartsInSingleFile()
{
AddCodeFile("test.cs", @"
partial class SimpleClass { void Member1() {} }
partial class SimpleClass { void Member2() {} }");
Assert.AreEqual(1, topLevelTypeModels.Count);
topLevelChangeEventArgs.Clear(); // clear for follow-up tests
}
[Test]
public void UpdateTwoPartsInSingleFile()
{
TwoPartsInSingleFile();
var simpleClass = topLevelTypeModels.Single();
UpdateCodeFile("test.cs", @"
partial class SimpleClass { void Member1b() {} }
partial class SimpleClass { void Member2b() {} }");
Assert.AreSame(simpleClass, topLevelTypeModels.Single());
Assert.AreEqual(new[] { "Member1b", "Member2b" }, simpleClass.Members.Select(m => m.Name).ToArray());
}
[Test]
public void RemoveOneOfPartsInSingleFile()
{
TwoPartsInSingleFile();
var simpleClass = topLevelTypeModels.Single();
var member1 = simpleClass.Members.First();
UpdateCodeFile("test.cs", "class SimpleClass { void Member1() {} }");
Assert.AreSame(simpleClass, topLevelTypeModels.Single());
Assert.AreSame(member1, simpleClass.Members.Single());
}
[Test]
public void RemoveBothPartsInSingleFile()
{
TwoPartsInSingleFile();
var simpleClass = topLevelTypeModels.Single();
UpdateCodeFile("test.cs", "class OtherClass {}");
Assert.AreNotSame(simpleClass, topLevelTypeModels.Single());
}
#endregion
#region TestRegionIsFromPrimaryPart
[Test]
public void AddingDesignerPartDoesNotChangeRegion()
{
AddCodeFile("Form.cs", "partial class MyForm {}");
Assert.AreEqual("Form.cs", topLevelTypeModels.Single().Region.FileName);
AddCodeFile("Form.Designer.cs", "partial class MyForm {}");
Assert.AreEqual("Form.cs", topLevelTypeModels.Single().Region.FileName);
}
[Test]
public void AddingPrimaryPartChangesRegion()
{
AddCodeFile("Form.Designer.cs", "partial class MyForm {}");
Assert.AreEqual("Form.Designer.cs", topLevelTypeModels.Single().Region.FileName);
AddCodeFile("Form.cs", "partial class MyForm {}");
Assert.AreEqual("Form.cs", topLevelTypeModels.Single().Region.FileName);
}
[Test]
public void RemovingPrimaryPartChangesRegionToNextBestPart()
{
AddCodeFile("Form.cs", "partial class MyForm { int primaryMember; }");
AddCodeFile("Form.Designer.Designer.cs", "partial class MyForm { int designer2; }");
var form = topLevelTypeModels.Single();
Assert.AreEqual(new[] { "primaryMember", "designer2" }, form.Members.Select(m => m.Name).ToArray());
AddCodeFile("Form.Designer.cs", "partial class MyForm { int designer; }");
Assert.AreEqual("Form.cs", form.Region.FileName);
Assert.AreEqual(new[] { "primaryMember", "designer", "designer2" }, form.Members.Select(m => m.Name).ToArray());
RemoveCodeFile("Form.cs");
Assert.AreSame(form, topLevelTypeModels.Single());
Assert.AreEqual("Form.Designer.cs", form.Region.FileName);
Assert.AreEqual(new[] { "designer", "designer2" }, form.Members.Select(m => m.Name).ToArray());
}
#endregion
}
}

6
src/Main/SharpDevelop/Dom/ModelFactory.cs

@ -15,7 +15,11 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -15,7 +15,11 @@ namespace ICSharpCode.SharpDevelop.Dom
public ITypeDefinitionModel CreateTypeDefinitionModel(IEntityModelContext context, params IUnresolvedTypeDefinition[] parts)
{
return new TypeDefinitionModel(context, parts);
var model = new TypeDefinitionModel(context, parts[0]);
for (int i = 1; i < parts.Length; i++) {
model.Update(null, parts[i]);
}
return model;
}
public IMemberModel CreateMemberModel(IEntityModelContext context, IUnresolvedMember member)

72
src/Main/SharpDevelop/Dom/TypeDefinitionModel.cs

@ -6,8 +6,8 @@ using System.Collections; @@ -6,8 +6,8 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
@ -21,32 +21,17 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -21,32 +21,17 @@ namespace ICSharpCode.SharpDevelop.Dom
{
readonly IEntityModelContext context;
readonly FullTypeName fullTypeName;
List<IUnresolvedTypeDefinition> parts;
List<IUnresolvedTypeDefinition> parts = new List<IUnresolvedTypeDefinition>();
public TypeDefinitionModel(IEntityModelContext context, params IUnresolvedTypeDefinition[] parts)
public TypeDefinitionModel(IEntityModelContext context, IUnresolvedTypeDefinition firstPart)
{
if (context == null)
throw new ArgumentNullException("context");
if (parts.Length == 0)
throw new ArgumentException("Number of parts must not be zero");
if (firstPart == null)
throw new ArgumentNullException("firstPart");
this.context = context;
this.parts = new List<IUnresolvedTypeDefinition>(parts);
MovePrimaryPartToFront();
this.fullTypeName = parts[0].FullTypeName;
}
void MovePrimaryPartToFront()
{
int bestPartIndex = 0;
for (int i = 1; i < parts.Count; i++) {
if (context.IsBetterPart(parts[i], parts[bestPartIndex]))
bestPartIndex = i;
}
IUnresolvedTypeDefinition primaryPart = parts[bestPartIndex];
for (int i = bestPartIndex; i > 0; i--) {
parts[i] = parts[i - 1];
}
parts[0] = primaryPart;
this.parts.Add(firstPart);
this.fullTypeName = firstPart.FullTypeName;
}
public IReadOnlyList<IUnresolvedTypeDefinition> Parts {
@ -106,7 +91,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -106,7 +91,7 @@ namespace ICSharpCode.SharpDevelop.Dom
this.parent = parent;
}
public void Insert(int partIndex, IUnresolvedTypeDefinition newPart)
public void InsertPart(int partIndex, IUnresolvedTypeDefinition newPart)
{
List<MemberModel> newItems = new List<MemberModel>(newPart.Members.Count);
foreach (var newMember in newPart.Members) {
@ -117,7 +102,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -117,7 +102,7 @@ namespace ICSharpCode.SharpDevelop.Dom
collectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems, GetCount(partIndex)));
}
public void Remove(int partIndex)
public void RemovePart(int partIndex)
{
var oldItems = lists[partIndex];
lists.RemoveAt(partIndex);
@ -125,7 +110,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -125,7 +110,7 @@ namespace ICSharpCode.SharpDevelop.Dom
collectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, GetCount(partIndex)));
}
public void Update(int partIndex, IUnresolvedTypeDefinition newPart)
public void UpdatePart(int partIndex, IUnresolvedTypeDefinition newPart)
{
List<MemberModel> list = lists[partIndex];
var newMembers = newPart.Members;
@ -222,6 +207,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -222,6 +207,9 @@ namespace ICSharpCode.SharpDevelop.Dom
MemberCollection members;
if (!membersWeakReference.TryGetTarget(out members)) {
members = new MemberCollection(this);
for (int i = 0; i < parts.Count; i++) {
members.InsertPart(i, parts[i]);
}
membersWeakReference.SetTarget(members);
}
return members;
@ -242,9 +230,41 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -242,9 +230,41 @@ namespace ICSharpCode.SharpDevelop.Dom
}
#endregion
#region Update
public void Update(IUnresolvedTypeDefinition oldPart, IUnresolvedTypeDefinition newPart)
{
throw new NotImplementedException();
SD.MainThread.VerifyAccess();
MemberCollection members;
membersWeakReference.TryGetTarget(out members);
if (oldPart == null) {
if (newPart == null) {
// nothing changed
return;
}
// Part added
int newPartIndex = 0;
while (newPartIndex < parts.Count && !context.IsBetterPart(newPart, parts[newPartIndex]))
newPartIndex++;
if (members != null)
members.InsertPart(newPartIndex, newPart);
parts.Insert(newPartIndex, newPart);
} else {
int partIndex = parts.IndexOf(oldPart);
if (partIndex < 0)
throw new ArgumentException("could not find old part");
if (newPart == null) {
// Part removed
parts.RemoveAt(partIndex);
if (members != null)
members.RemovePart(partIndex);
} else {
// Part updated
parts[partIndex] = newPart;
if (members != null)
members.UpdatePart(partIndex, newPart);
}
}
}
#endregion
}
}

Loading…
Cancel
Save