Browse Source

First attempt at document model.

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
04dbe33242
  1. 10
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestClass.cs
  2. 8
      src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestMethod.cs
  3. 164
      src/Main/Base/Project/Dom/ConcatModelCollection.cs
  4. 40
      src/Main/Base/Project/Dom/IEntityModel.cs
  5. 79
      src/Main/Base/Project/Dom/IEntityModelContext.cs
  6. 27
      src/Main/Base/Project/Dom/IMemberModel.cs
  7. 2
      src/Main/Base/Project/Dom/IModelCollection.cs
  8. 15
      src/Main/Base/Project/Dom/IModelService.cs
  9. 31
      src/Main/Base/Project/Dom/ITypeDefinitionModel.cs
  10. 69
      src/Main/Base/Project/Dom/ITypeDefinitionModelCollection.cs
  11. 47
      src/Main/Base/Project/Dom/KeyedModelCollection.cs
  12. 8
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  13. 8
      src/Main/Base/Project/Src/Project/AbstractProject.cs
  14. 8
      src/Main/Base/Project/Src/Project/IProject.cs
  15. 82
      src/Main/Base/Test/Dom/CSharpModelTests.cs
  16. 42
      src/Main/Base/Test/Dom/ProjectEntityModelContextTests.cs
  17. 7
      src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj
  18. 13
      src/Main/Base/Test/Utils/AssemblyLoader.cs
  19. 10
      src/Main/ICSharpCode.SharpDevelop.Widgets/Project/MyersDiff/MyersDiffAlgorithm.cs
  20. 105
      src/Main/SharpDevelop/Dom/MemberModel.cs
  21. 52
      src/Main/SharpDevelop/Dom/TopLevelTypeDefinitionModelCollection.cs
  22. 264
      src/Main/SharpDevelop/Dom/TypeDefinitionModel.cs
  23. 3
      src/Main/SharpDevelop/SharpDevelop.csproj

10
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestClass.cs

@ -4,9 +4,12 @@ @@ -4,9 +4,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Widgets;
namespace ICSharpCode.UnitTesting
@ -67,7 +70,12 @@ namespace ICSharpCode.UnitTesting @@ -67,7 +70,12 @@ namespace ICSharpCode.UnitTesting
public ITypeDefinition Resolve()
{
ICompilation compilation = SD.ParserService.GetCompilation(parentProject.Project);
return Resolve(SD.ParserService.GetCurrentSolutionSnapshot());
}
public ITypeDefinition Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
ICompilation compilation = solutionSnapshot.GetCompilation(parentProject.Project);
IType type = compilation.MainAssembly.GetTypeDefinition(fullTypeName);
return type.GetDefinition();
}

8
src/AddIns/Analysis/UnitTesting/NUnit/NUnitTestMethod.cs

@ -5,6 +5,7 @@ using System; @@ -5,6 +5,7 @@ using System;
using System.Windows.Input;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Widgets;
namespace ICSharpCode.UnitTesting
@ -80,7 +81,12 @@ namespace ICSharpCode.UnitTesting @@ -80,7 +81,12 @@ namespace ICSharpCode.UnitTesting
public IMethod Resolve()
{
ICompilation compilation = SD.ParserService.GetCompilation(parentProject.Project);
return Resolve(SD.ParserService.GetCurrentSolutionSnapshot());
}
public IMethod Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
ICompilation compilation = solutionSnapshot.GetCompilation(parentProject.Project);
IMethod resolvedMethod = method.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly));
return resolvedMethod;
}

164
src/Main/Base/Project/Dom/ConcatModelCollection.cs

@ -0,0 +1,164 @@ @@ -0,0 +1,164 @@
// 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.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// A <see cref="IModelCollection{T}"/> that works by concatening multiple
/// other model collections.
/// </summary>
public sealed class ConcatModelCollection<T> : IModelCollection<T>
{
sealed class InputCollection : Collection<IModelCollection<T>>
{
readonly ConcatModelCollection<T> owner;
public InputCollection(ConcatModelCollection<T> owner)
{
this.owner = owner;
}
protected override void ClearItems()
{
if (owner.collectionChanged != null) {
foreach (var input in Items) {
input.CollectionChanged -= owner.OnInputCollectionChanged;
}
}
base.ClearItems();
owner.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void InsertItem(int index, IModelCollection<T> item)
{
if (owner.collectionChanged != null)
item.CollectionChanged += owner.OnInputCollectionChanged;
base.InsertItem(index, item);
owner.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add, item.ToArray(), owner.GetCount(index)));
}
protected override void RemoveItem(int index)
{
if (owner.collectionChanged != null)
Items[index].CollectionChanged -= owner.OnInputCollectionChanged;
var oldItems = Items[index].ToArray();
base.RemoveItem(index);
owner.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove, oldItems, owner.GetCount(index)));
}
protected override void SetItem(int index, IModelCollection<T> item)
{
RemoveItem(index);
InsertItem(index, item);
}
}
InputCollection inputs;
public ConcatModelCollection()
{
this.inputs = new InputCollection(this);
}
public IList<IModelCollection<T>> Inputs {
get { return inputs; }
}
NotifyCollectionChangedEventHandler collectionChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged {
add {
var oldEventHandlers = collectionChanged;
collectionChanged += value;
if (oldEventHandlers == null && collectionChanged != null) {
foreach (var input in inputs)
input.CollectionChanged += OnInputCollectionChanged;
}
}
remove {
var oldEventHandlers = collectionChanged;
collectionChanged -= value;
if (oldEventHandlers != null && collectionChanged == null) {
foreach (var input in inputs)
input.CollectionChanged -= OnInputCollectionChanged;
}
}
}
void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (collectionChanged != null)
collectionChanged(this, e);
}
void OnInputCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
int inputIndex = inputs.IndexOf((IModelCollection<T>)sender);
int startIndex = GetCount(inputIndex);
NotifyCollectionChangedEventArgs newEventArgs;
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.NewItems, startIndex + e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.OldItems, startIndex + e.OldStartingIndex);
break;
case NotifyCollectionChangedAction.Replace:
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.OldItems, e.NewItems, startIndex + e.OldStartingIndex);
break;
case NotifyCollectionChangedAction.Move:
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.OldItems, startIndex + e.OldStartingIndex, startIndex + e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Reset:
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action);
break;
default:
throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction");
}
collectionChanged(this, newEventArgs);
}
public int Count {
get { return GetCount(inputs.Count); }
}
int GetCount(int inputIndex)
{
int count = 0;
for (int i = 0; i < inputIndex; i++) {
count += inputs[i].Count;
}
return count;
}
public IEnumerator<T> GetEnumerator()
{
return inputs.SelectMany(i => i).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public T this[int index] {
get {
int inputIndex = 0;
while (index >= inputs[inputIndex].Count) {
index -= inputs[inputIndex].Count;
inputIndex++;
}
return inputs[inputIndex][index];
}
}
}
}

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

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
// 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.ComponentModel;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// An NRefactory entity as a model.
/// </summary>
public interface IEntityModel : INotifyPropertyChanged
{
/// <summary>
/// Gets the parent project that contains this entity.
/// May return null if the entity is not part of a project.
/// </summary>
IProject ParentProject { get; }
/// <summary>
/// Gets the region where this entity is defined.
/// </summary>
DomRegion Region { get; }
/// <summary>
/// Resolves the entity in the current solution snapshot.
/// Returns null if the entity could not be resolved.
/// </summary>
IEntity Resolve();
/// <summary>
/// Resolves the entity in the specified solution snapshot.
/// Returns null if the entity could not be resolved.
/// </summary>
IEntity Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot);
}
}

79
src/Main/Base/Project/Dom/IEntityModelContext.cs

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// The context for an entity model.
/// This may be a reference to a project, or a compilation provider for a single stand-alone code file.
/// </summary>
public interface IEntityModelContext
{
/// <summary>
/// Used for <see cref="IEntityModel.ParentProject"/>.
/// </summary>
IProject Project { get; }
/// <summary>
/// Used for <see cref="IEntityModel.Resolve()"/>.
/// </summary>
/// <param name="solutionSnapshot">
/// The solution snapshot provided to <see cref="IEntityModel.Resolve(ISolutionSnapshotWithProjectMapping)"/>,
/// or null if the <see cref="IEntityModel.Resolve()"/> overload was used.
/// </param>
ICompilation GetCompilation(ISolutionSnapshotWithProjectMapping solutionSnapshot);
/// <summary>
/// Returns true if part1 is considered a better candidate for the primary part than part2.
/// </summary>
bool IsBetterPart(IUnresolvedTypeDefinition part1, IUnresolvedTypeDefinition part2);
}
public class ProjectEntityModelContext : IEntityModelContext
{
readonly IProject project;
readonly string primaryCodeFileExtension;
public ProjectEntityModelContext(IProject project, string primaryCodeFileExtension)
{
if (project == null)
throw new ArgumentNullException("project");
this.project = project;
this.primaryCodeFileExtension = primaryCodeFileExtension;
}
public IProject Project {
get { return project; }
}
public ICompilation GetCompilation(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
if (solutionSnapshot != null)
return solutionSnapshot.GetCompilation(project);
else
return SD.ParserService.GetCompilation(project);
}
public bool IsBetterPart(IUnresolvedTypeDefinition part1, IUnresolvedTypeDefinition part2)
{
IUnresolvedFile file1 = part1.UnresolvedFile;
IUnresolvedFile file2 = part2.UnresolvedFile;
if (file1 != null && file2 == null)
return true;
if (file1 == null)
return false;
bool file1HasExtension = file1.FileName.EndsWith(primaryCodeFileExtension, StringComparison.OrdinalIgnoreCase);
bool file2HasExtension = file2.FileName.EndsWith(primaryCodeFileExtension, StringComparison.OrdinalIgnoreCase);
if (file1HasExtension && !file2HasExtension)
return true;
if (!file1HasExtension && file2HasExtension)
return false;
return file1.FileName.Length < file2.FileName.Length;
}
}
}

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

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Observable model for a member.
/// </summary>
public interface IMemberModel : IEntityModel
{
/// <summary>
/// Resolves the member in the current solution snapshot.
/// Returns null if the member could not be resolved.
/// </summary>
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);
}
}

2
src/Main/Base/Project/Dom/IModelCollection.cs

@ -10,7 +10,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -10,7 +10,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// <summary>
/// A read-only collection that provides change notifications.
/// </summary>
public interface IModelCollection<out T> : IReadOnlyCollection<T>, INotifyCollectionChanged
public interface IModelCollection<out T> : IReadOnlyList<T>, INotifyCollectionChanged
{
}
}

15
src/Main/Base/Project/Dom/IModelService.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Service that enables lookup of model objects from NRefactory objects.
/// </summary>
public interface IModelService
{
}
}

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

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Observable model for a type definition.
/// </summary>
public interface ITypeDefinitionModel : IEntityModel
{
FullTypeName FullTypeName { get; }
IModelCollection<ITypeDefinitionModel> NestedTypes { get; }
IModelCollection<IMemberModel> Members { get; }
/// <summary>
/// Resolves the type definition in the current solution snapshot.
/// Returns null if the type definition could not be resolved.
/// </summary>
new ITypeDefinition Resolve();
/// <summary>
/// Resolves the type definition in the specified solution snapshot.
/// Returns null if the type definition could not be resolved.
/// </summary>
new ITypeDefinition Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot);
}
}

69
src/Main/Base/Project/Dom/ITypeDefinitionModelCollection.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
// 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.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// A collection of type definition models with the ability to look them up by name.
/// </summary>
public interface ITypeDefinitionModelCollection : IModelCollection<ITypeDefinitionModel>
{
/// <summary>
/// Gets the type definition model with the specified type name.
/// Returns null if no such model object exists.
/// </summary>
ITypeDefinitionModel this[FullTypeName fullTypeName] { get; }
/// <summary>
/// Gets the type definition model with the specified type name.
/// Returns null if no such model object exists.
/// </summary>
ITypeDefinitionModel this[TopLevelTypeName topLevelTypeName] { get; }
}
public sealed class EmptyTypeDefinitionModelCollection : ITypeDefinitionModelCollection
{
public static readonly EmptyTypeDefinitionModelCollection Instance = new EmptyTypeDefinitionModelCollection();
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged {
add { }
remove { }
}
ITypeDefinitionModel ITypeDefinitionModelCollection.this[FullTypeName name] {
get { return null; }
}
ITypeDefinitionModel ITypeDefinitionModelCollection.this[TopLevelTypeName name] {
get { return null; }
}
ITypeDefinitionModel IReadOnlyList<ITypeDefinitionModel>.this[int index] {
get {
throw new ArgumentOutOfRangeException();
}
}
int IReadOnlyCollection<ITypeDefinitionModel>.Count {
get { return 0; }
}
IEnumerator<ITypeDefinitionModel> IEnumerable<ITypeDefinitionModel>.GetEnumerator()
{
return Enumerable.Empty<ITypeDefinitionModel>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Enumerable.Empty<ITypeDefinitionModel>().GetEnumerator();
}
}
}

47
src/Main/Base/Project/Dom/KeyedModelCollection.cs

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
// 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.Collections.ObjectModel;
using System.Collections.Specialized;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Observable KeyedCollection.
/// </summary>
public abstract class KeyedModelCollection<TKey, TItem> : KeyedCollection<TKey, TItem>, IModelCollection<TItem>
{
protected override void ClearItems()
{
base.ClearItems();
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void InsertItem(int index, TItem item)
{
base.InsertItem(index, item);
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void RemoveItem(int index)
{
var oldItem = Items[index];
base.RemoveItem(index);
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
}
protected override void SetItem(int index, TItem item)
{
var oldItem = Items[index];
base.SetItem(index, item);
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
}

8
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -95,6 +95,14 @@ @@ -95,6 +95,14 @@
<Compile Include="..\..\ICSharpCode.SharpDevelop.BuildWorker\ExtendedBinaryReader.cs">
<Link>Src\Project\MSBuildEngine\ExtendedBinaryReader.cs</Link>
</Compile>
<Compile Include="Dom\ConcatModelCollection.cs" />
<Compile Include="Dom\IEntityModelContext.cs" />
<Compile Include="Dom\IMemberModel.cs" />
<Compile Include="Dom\IModelService.cs" />
<Compile Include="Dom\ITypeDefinitionModel.cs" />
<Compile Include="Dom\ITypeDefinitionModelCollection.cs" />
<Compile Include="Dom\KeyedModelCollection.cs" />
<Compile Include="Dom\IEntityModel.cs" />
<Compile Include="Dom\IModelCollection.cs" />
<Compile Include="Dom\ITreeNodeFactory.cs" />
<Compile Include="Editor\DocumentServiceAttribute.cs" />

8
src/Main/Base/Project/Src/Project/AbstractProject.cs

@ -11,10 +11,10 @@ using System.Linq; @@ -11,10 +11,10 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Gui.OptionPanels;
using ICSharpCode.SharpDevelop.Parser;
@ -671,5 +671,11 @@ namespace ICSharpCode.SharpDevelop.Project @@ -671,5 +671,11 @@ namespace ICSharpCode.SharpDevelop.Project
return false;
}
}
public virtual ICSharpCode.SharpDevelop.Dom.ITypeDefinitionModelCollection TypeDefinitionModels {
get {
return EmptyTypeDefinitionModelCollection.Instance;
}
}
}
}

8
src/Main/Base/Project/Src/Project/IProject.cs

@ -9,8 +9,10 @@ using System.ComponentModel; @@ -9,8 +9,10 @@ using System.ComponentModel;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Parser;
@ -319,6 +321,12 @@ namespace ICSharpCode.SharpDevelop.Project @@ -319,6 +321,12 @@ namespace ICSharpCode.SharpDevelop.Project
/// This method is called by the parser service <b>within a per-file lock</b>.
/// </summary>
void OnParseInformationUpdated(ParseInformationEventArgs args);
/// <summary>
/// Gets the models for the top-level type definitions in this project.
/// Never returns null, but may return a permanently empty collection if this project does not support such models.
/// </summary>
ITypeDefinitionModelCollection TypeDefinitionModels { get; }
}
/// <summary>

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

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
// 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 ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
using NUnit.Framework;
using Rhino.Mocks;
namespace ICSharpCode.SharpDevelop.Dom
{
[TestFixture]
public class CSharpModelTests
{
IProject project;
IProjectContent projectContent;
IEntityModelContext context;
TopLevelTypeDefinitionModelCollection topLevelTypeModels;
#region SetUp and other helper methods
[SetUp]
public virtual void SetUp()
{
SD.InitializeForUnitTests();
SD.Services.AddStrictMockService<IParserService>();
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());
}
[TearDown]
public virtual void TearDown()
{
SD.TearDownForUnitTests();
}
protected void AddCodeFile(string fileName, string code)
{
var oldFile = projectContent.GetFile(fileName);
Assert.IsNull(oldFile);
var newFile = Parse(fileName, code);
projectContent = projectContent.AddOrUpdateFiles(newFile);
topLevelTypeModels.NotifyParseInformationChanged(oldFile, newFile);
}
IUnresolvedFile Parse(string fileName, string code)
{
var parser = new CSharpParser();
var syntaxTree = parser.Parse(code, fileName);
Assert.IsFalse(parser.HasErrors);
return syntaxTree.ToTypeSystem();
}
protected void UpdateCodeFile(string fileName, string code)
{
var oldFile = projectContent.GetFile(fileName);
Assert.IsNotNull(oldFile);
var newFile = Parse(fileName, code);
projectContent = projectContent.AddOrUpdateFiles(newFile);
topLevelTypeModels.NotifyParseInformationChanged(oldFile, newFile);
}
protected void RemoveCodeFile(string fileName)
{
var oldFile = projectContent.GetFile(fileName);
projectContent = projectContent.RemoveFiles(fileName);
topLevelTypeModels.NotifyParseInformationChanged(oldFile, null);
}
#endregion
[Test]
public void EmptyProject()
{
Assert.AreEqual(0, topLevelTypeModels.Count);
}
}
}

42
src/Main/Base/Test/Dom/ProjectEntityModelContextTests.cs

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Project;
using NUnit.Framework;
using Rhino.Mocks;
namespace ICSharpCode.SharpDevelop.Dom
{
[TestFixture]
public class ProjectEntityModelContextTests
{
IUnresolvedTypeDefinition CreateMockTypeDefinition(string fileName)
{
var typeDefinition = MockRepository.GenerateStrictMock<IUnresolvedTypeDefinition>();
var file = MockRepository.GenerateStrictMock<IUnresolvedFile>();
file.Stub(f => f.FileName).Return(fileName);
typeDefinition.Stub(td => td.UnresolvedFile).Return(file);
return typeDefinition;
}
[Test]
public void XamlCodeBehindIsBetterThanXaml()
{
var context = new ProjectEntityModelContext(MockRepository.GenerateStrictMock<IProject>(), ".cs");
Assert.IsTrue(context.IsBetterPart(CreateMockTypeDefinition("Window.xaml.cs"), CreateMockTypeDefinition("Window.xaml")));
Assert.IsTrue(context.IsBetterPart(CreateMockTypeDefinition("Window.cs"), CreateMockTypeDefinition("Window.xaml")));
Assert.IsFalse(context.IsBetterPart(CreateMockTypeDefinition("Window.xaml"), CreateMockTypeDefinition("Window.xaml.cs")));
Assert.IsFalse(context.IsBetterPart(CreateMockTypeDefinition("Window.xaml"), CreateMockTypeDefinition("Window.cs")));
}
[Test]
public void MainPartIsBetterThanDesigner()
{
var context = new ProjectEntityModelContext(MockRepository.GenerateStrictMock<IProject>(), ".cs");
Assert.IsTrue(context.IsBetterPart(CreateMockTypeDefinition("Form.cs"), CreateMockTypeDefinition("Form.Designer.cs")));
Assert.IsFalse(context.IsBetterPart(CreateMockTypeDefinition("Form.Designer.cs"), CreateMockTypeDefinition("Form.cs")));
}
}
}

7
src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj

@ -78,6 +78,8 @@ @@ -78,6 +78,8 @@
<Compile Include="AssemblyInfo.cs" />
<Compile Include="CheckAssemblyFlags.cs" />
<Compile Include="CodeConverterTests.cs" />
<Compile Include="Dom\CSharpModelTests.cs" />
<Compile Include="Dom\ProjectEntityModelContextTests.cs" />
<Compile Include="Dom\TreeNodeFactoryServiceTests.cs" />
<Compile Include="ExceptionClassOverridesTestFixture.cs" />
<Compile Include="GenerateOverrideMethodTests.cs" />
@ -114,6 +116,7 @@ @@ -114,6 +116,7 @@
<Compile Include="StringTagProvider\MockProjectForTagProvider.cs" />
<Compile Include="StringTagProvider\NullProjectStringTagProviderTestFixture.cs" />
<Compile Include="StringTagProvider\ProjectTagsTestFixture.cs" />
<Compile Include="Utils\AssemblyLoader.cs" />
<Compile Include="Utils\FakeMessageLoop.cs" />
<Compile Include="Utils\MockAssembly.cs" />
<Compile Include="Utils\MockComponent.cs" />
@ -160,6 +163,10 @@ @@ -160,6 +163,10 @@
<Project>{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}</Project>
<Name>ICSharpCode.AvalonEdit</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj">
<Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project>
<Name>ICSharpCode.NRefactory.CSharp</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name>

13
src/Main/Base/Test/Utils/AssemblyLoader.cs

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
// 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 ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.SharpDevelop
{
public static class AssemblyLoader
{
public static readonly IUnresolvedAssembly Corlib = new CecilLoader { LazyLoad = true }.LoadAssemblyFile(typeof(object).Assembly.Location);
}
}

10
src/Main/ICSharpCode.SharpDevelop.Widgets/Project/MyersDiff/MyersDiffAlgorithm.cs

@ -59,11 +59,11 @@ namespace ICSharpCode.SharpDevelop.Widgets.MyersDiff @@ -59,11 +59,11 @@ namespace ICSharpCode.SharpDevelop.Widgets.MyersDiff
///
/// Example:
///
/// H E L L O W O R L D
/// ____
/// L \___
/// O \___
/// W \________
/// H E L L O W O R L D
/// ____
/// L \___
/// O \___
/// W \________
///
/// Since every D-path has exactly D horizontal or vertical elements, it can
/// only end on the diagonals -D, -D+2, ..., D-2, D.

105
src/Main/SharpDevelop/Dom/MemberModel.cs

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
// 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.ComponentModel;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// A mutable class that can track a member as the solution is being changed.
/// </summary>
sealed class MemberModel : IMemberModel
{
/// <summary>
/// A strong reference to the parent TypeDefinitionModel.Members collection.
/// This is necessary to prevent the garbage collector from
/// freeing the weak reference to the collection while one of the member models
/// is still in use.
/// If we don't prevent this, the type definition model might create multiple
/// MemberModels for the same member.
/// </summary>
internal IModelCollection<MemberModel> strongParentCollectionReference;
readonly IEntityModelContext context;
IUnresolvedMember member;
public MemberModel(IEntityModelContext context, IUnresolvedMember member)
{
if (context == null)
throw new ArgumentNullException("context");
if (member == null)
throw new ArgumentNullException("member");
this.context = context;
this.member = member;
}
public event PropertyChangedEventHandler PropertyChanged { add {} remove {} }
public void Update(IUnresolvedMember newMember)
{
if (newMember == null)
throw new ArgumentNullException("newMember");
this.member = newMember;
}
public IProject ParentProject {
get { return context.Project; }
}
public IUnresolvedMember UnresolvedMember {
get { return member; }
}
public EntityType EntityType {
get { return member.EntityType; }
}
public DomRegion Region {
get { return member.Region; }
}
public string Name {
get { return member.Name; }
}
/// <summary>
/// Gets the full type name of the type that declares this member.
/// </summary>
public FullTypeName DeclaringTypeName {
get {
if (member.DeclaringTypeDefinition != null)
return member.DeclaringTypeDefinition.FullTypeName;
else
return new TopLevelTypeName(string.Empty, string.Empty);
}
}
#region Resolve
public IMember Resolve()
{
var compilation = context.GetCompilation(null);
return member.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly));
}
public IMember Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
var compilation = context.GetCompilation(solutionSnapshot);
return member.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly));
}
IEntity IEntityModel.Resolve()
{
return Resolve();
}
IEntity IEntityModel.Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
return Resolve(solutionSnapshot);
}
#endregion
}
}

52
src/Main/SharpDevelop/Dom/TopLevelTypeDefinitionModelCollection.cs

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
// 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.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections.Specialized;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// A TypeDefinitionModel-collection that holds models for all top-level types in a project content.
/// </summary>
sealed class TopLevelTypeDefinitionModelCollection : KeyedModelCollection<TopLevelTypeName, TypeDefinitionModel>
{
readonly IEntityModelContext context;
public TopLevelTypeDefinitionModelCollection(IEntityModelContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public TypeDefinitionModel this[FullTypeName fullTypeName] {
get {
TypeDefinitionModel model = base[fullTypeName.TopLevelTypeName];
for (int i = 0; i < fullTypeName.NestingLevel; i++) {
throw new NotImplementedException();
}
return model;
}
}
/// <summary>
/// Updates the parse information.
/// </summary>
public void NotifyParseInformationChanged(IUnresolvedFile oldFile, IUnresolvedFile newFile)
{
if (oldFile != null) {
}
}
protected override TopLevelTypeName GetKeyForItem(TypeDefinitionModel item)
{
return item.FullTypeName.TopLevelTypeName;
}
}
}

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

@ -0,0 +1,264 @@ @@ -0,0 +1,264 @@
// 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.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// A mutable class that can track a type definition as the solution is being changed.
/// </summary>
sealed class TypeDefinitionModel : ITypeDefinitionModel
{
readonly IEntityModelContext context;
readonly FullTypeName fullTypeName;
List<IUnresolvedTypeDefinition> parts;
public TypeDefinitionModel(IEntityModelContext context, params IUnresolvedTypeDefinition[] parts)
{
if (context == null)
throw new ArgumentNullException("context");
if (parts.Length == 0)
throw new ArgumentException("Number of parts must not be zero");
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;
}
/*
/// <summary>
/// Updates the type definition model by removing the old parts and adding the new ones.
/// </summary>
public void Update(IReadOnlyList<IUnresolvedTypeDefinition> removedParts, IReadOnlyList<IUnresolvedTypeDefinition> newParts)
{
SD.MainThread.VerifyAccess();
if (removedParts != null)
foreach (var p in removedParts)
parts.Remove(p);
if (newParts != null)
parts.AddRange(newParts);
MemberModelCollection members;
if (membersWeakReference.TryGetTarget(out members)) {
members.Update(parts);
}
}*/
public IProject ParentProject {
get { return context.Project; }
}
public FullTypeName FullTypeName {
get { return fullTypeName; }
}
public DomRegion Region {
get { return parts[0].Region; }
}
public string Name {
get { return fullTypeName.Name; }
}
#region Resolve
public ITypeDefinition Resolve()
{
var compilation = context.GetCompilation(null);
return compilation.MainAssembly.GetTypeDefinition(fullTypeName);
}
public ITypeDefinition Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
var compilation = context.GetCompilation(solutionSnapshot);
return compilation.MainAssembly.GetTypeDefinition(fullTypeName);
}
IEntity IEntityModel.Resolve()
{
return Resolve();
}
IEntity IEntityModel.Resolve(ISolutionSnapshotWithProjectMapping solutionSnapshot)
{
return Resolve(solutionSnapshot);
}
#endregion
public event PropertyChangedEventHandler PropertyChanged { add {} remove {} }
#region Members collection
sealed class MemberCollection : IModelCollection<MemberModel>
{
readonly TypeDefinitionModel parent;
List<List<MemberModel>> lists = new List<List<MemberModel>>();
public MemberCollection(TypeDefinitionModel parent)
{
this.parent = parent;
}
public void Insert(int partIndex, IUnresolvedTypeDefinition newPart)
{
List<MemberModel> newItems = new List<MemberModel>(newPart.Members.Count);
foreach (var newMember in newPart.Members) {
newItems.Add(new MemberModel(parent.context, newMember) { strongParentCollectionReference = this });
}
lists.Insert(partIndex, newItems);
if (collectionChanged != null)
collectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems, GetCount(partIndex)));
}
public void Remove(int partIndex)
{
var oldItems = lists[partIndex];
lists.RemoveAt(partIndex);
if (collectionChanged != null)
collectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, GetCount(partIndex)));
}
public void Update(int partIndex, IUnresolvedTypeDefinition newPart)
{
List<MemberModel> list = lists[partIndex];
var newMembers = newPart.Members;
int startPos = 0;
// Look at the initial members and update them if they're matching
while (startPos < list.Count && startPos < newMembers.Count && IsMatch(list[startPos], newMembers[startPos])) {
list[startPos].Update(newMembers[startPos]);
startPos++;
}
// Look at the final members
int endPosOld = list.Count - 1;
int endPosNew = newMembers.Count - 1;
while (endPosOld >= startPos && endPosNew >= startPos && IsMatch(list[endPosOld], newMembers[endPosNew])) {
list[endPosOld--].Update(newMembers[endPosNew--]);
}
// [startPos, endPos] is the middle portion that contains all the changes
// Add one to endPos so that it's the exclusive end of the middle portion:
endPosOld++;
endPosNew++;
// [startPos, endPos)
// Now we still need to update the members in between.
// We might try to be clever here and find a LCS so that we only update the members that were actually changed,
// or we might consider moving members around (INotifyCollectionChanged supports moves)
// However, the easiest solution by far is to just remove + readd the whole middle portion.
var oldItems = collectionChanged != null ? list.GetRange(startPos, endPosOld - startPos) : null;
list.RemoveRange(startPos, endPosOld - startPos);
var newItems = new MemberModel[endPosNew - startPos];
for (int i = 0; i < newItems.Length; i++) {
newItems[i] = new MemberModel(parent.context, newMembers[startPos + i]);
newItems[i].strongParentCollectionReference = this;
}
list.InsertRange(startPos, newItems);
if (collectionChanged != null)
collectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, oldItems, GetCount(partIndex) + startPos));
}
static bool IsMatch(MemberModel memberModel, IUnresolvedMember newMember)
{
return memberModel.EntityType == newMember.EntityType && memberModel.Name == newMember.Name;
}
NotifyCollectionChangedEventHandler collectionChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged {
add {
collectionChanged += value;
// Set strong reference to collection while there are event listeners
if (collectionChanged != null)
parent.membersStrongReference = this;
}
remove {
collectionChanged -= value;
if (collectionChanged == null)
parent.membersStrongReference = null;
}
}
int GetCount(int partIndex)
{
int count = 0;
for (int i = 0; i < partIndex; i++) {
count += lists[i].Count;
}
return count;
}
public int Count {
get { return GetCount(lists.Count); }
}
public IEnumerator<MemberModel> GetEnumerator()
{
return lists.SelectMany(i => i).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public MemberModel this[int index] {
get {
int inputIndex = 0;
while (index >= lists[inputIndex].Count) {
index -= lists[inputIndex].Count;
inputIndex++;
}
return lists[inputIndex][index];
}
}
}
WeakReference<MemberCollection> membersWeakReference = new WeakReference<MemberCollection>(null);
/// <summary>
/// Used to keep the member model collection alive while there are event listeners.
/// This is necessary to prevent us from creating multiple member models for the same member.
/// </summary>
MemberCollection membersStrongReference;
public IModelCollection<IMemberModel> Members {
get {
SD.MainThread.VerifyAccess();
MemberCollection members;
if (!membersWeakReference.TryGetTarget(out members)) {
members = new MemberCollection(this);
membersWeakReference.SetTarget(members);
}
return members;
}
}
#endregion
public IModelCollection<ITypeDefinitionModel> NestedTypes {
get {
throw new NotImplementedException();
}
}
}
}

3
src/Main/SharpDevelop/SharpDevelop.csproj

@ -81,7 +81,10 @@ @@ -81,7 +81,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Dom\MemberModel.cs" />
<Compile Include="Dom\TopLevelTypeDefinitionModelCollection.cs" />
<Compile Include="Dom\TreeNodeFactoryService.cs" />
<Compile Include="Dom\TypeDefinitionModel.cs" />
<Compile Include="OptionPanels\LoadSaveOptions.xaml.cs">
<DependentUpon>LoadSaveOptions.xaml</DependentUpon>
<SubType>Code</SubType>

Loading…
Cancel
Save