diff --git a/src/Main/Base/Project/Editor/CodeCompletion/ICompletionItem.cs b/src/Main/Base/Project/Editor/CodeCompletion/ICompletionItem.cs
index 068bacde20..c7871342b7 100644
--- a/src/Main/Base/Project/Editor/CodeCompletion/ICompletionItem.cs
+++ b/src/Main/Base/Project/Editor/CodeCompletion/ICompletionItem.cs
@@ -56,6 +56,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
public virtual void Complete(CompletionContext context)
{
context.Editor.Document.Replace(context.StartOffset, context.Length, this.Text);
+ // In case someone calls base.Complete() and then continues using the context, update EndOffset:
context.EndOffset = context.StartOffset + this.Text.Length;
}
}
diff --git a/src/Main/Base/Project/Project/ISolutionItem.cs b/src/Main/Base/Project/Project/ISolutionItem.cs
index 23eacad503..dca469f672 100644
--- a/src/Main/Base/Project/Project/ISolutionItem.cs
+++ b/src/Main/Base/Project/Project/ISolutionItem.cs
@@ -36,7 +36,8 @@ namespace ICSharpCode.SharpDevelop.Project
///
/// Gets the ID GUID of this solution item.
///
- Guid IdGuid { get; }
+ /// SharpDevelop will change an item's GUID in order to automatically solve GUID conflicts.
+ Guid IdGuid { get; set; }
///
/// Gets the type GUID of this solution item.
diff --git a/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs b/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
index 01e8633922..a58e262b50 100644
--- a/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
+++ b/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
@@ -122,6 +122,19 @@ namespace ICSharpCode.SharpDevelop.Project
public event EventHandler MinimumSolutionVersionChanged;
+ public override Guid IdGuid {
+ get {
+ return base.IdGuid;
+ }
+ set {
+ if (base.IdGuid != value) {
+ base.IdGuid = value;
+ // Save changed GUID to project file
+ SetPropertyInternal(null, null, ProjectGuidPropertyName, value.ToString("B").ToUpperInvariant(), PropertyStorageLocations.Base, true);
+ }
+ }
+ }
+
public string ToolsVersion {
get { return projectFile.ToolsVersion; }
protected internal set {
@@ -181,6 +194,7 @@ namespace ICSharpCode.SharpDevelop.Project
else
SetProperty(null, platform, "PlatformTarget", "AnyCPU", PropertyStorageLocations.PlatformSpecific, false);
LoadConfigurationPlatformNamesFromMSBuild();
+ isLoading = false;
}
///
@@ -914,7 +928,7 @@ namespace ICSharpCode.SharpDevelop.Project
propertyRemoved = true;
}
}
- if (propertyRemoved && propertyGroup.Children.Count() == 0)
+ if (propertyRemoved && propertyGroup.Children.Count == 0)
project.RemoveChild(propertyGroup);
}
}
@@ -1188,7 +1202,7 @@ namespace ICSharpCode.SharpDevelop.Project
#endregion
#region Loading
- protected bool isLoading;
+ protected bool isLoading = true;
public MSBuildBasedProject(ProjectLoadInformation loadInformation)
: base(loadInformation)
@@ -1197,7 +1211,6 @@ namespace ICSharpCode.SharpDevelop.Project
this.configurationNames = new MSBuildConfigurationOrPlatformNameCollection(this, false);
this.platformNames = new MSBuildConfigurationOrPlatformNameCollection(this, true);
- isLoading = true;
bool success = false;
try {
try {
@@ -1277,6 +1290,15 @@ namespace ICSharpCode.SharpDevelop.Project
userProjectFile = ProjectRootElement.Create(userFileName, MSBuildProjectCollection);
}
+ // Read IdGuid from project file.
+ string idGuidString = GetEvaluatedProperty(ProjectGuidPropertyName);
+ if (idGuidString != null) {
+ Guid idGuid;
+ if (Guid.TryParse(idGuidString, out idGuid)) {
+ // Use 'base.' to avoid writing the changed ID back into the project file
+ base.IdGuid = idGuid;
+ }
+ }
CreateItemsListFromMSBuild();
LoadConfigurationPlatformNamesFromMSBuild();
}
diff --git a/src/Main/Base/Test/Project/AddExistingProjectTests.cs b/src/Main/Base/Test/Project/AddExistingProjectTests.cs
index 4857d91822..63788e0882 100644
--- a/src/Main/Base/Test/Project/AddExistingProjectTests.cs
+++ b/src/Main/Base/Test/Project/AddExistingProjectTests.cs
@@ -30,7 +30,8 @@ namespace ICSharpCode.SharpDevelop.Project
IProject LoadProject(ProjectLoadInformation info)
{
var project = MockRepository.GenerateStrictMock();
- project.Stub(p => p.IdGuid).Return(projectGuid);
+ project.Stub(p => p.IdGuid).PropertyBehavior();
+ project.IdGuid = projectGuid;
project.Stub(p => p.FileName).Return(info.FileName);
project.Stub(p => p.ParentSolution).Return(info.Solution);
project.Stub(p => p.ParentFolder).PropertyBehavior();
@@ -67,10 +68,14 @@ namespace ICSharpCode.SharpDevelop.Project
solution.AddExistingProject(project1FileName);
}
-// [Test]
-// public void AddTwoProjectsWithSameGUID()
-// {
-//
-// }
+ [Test]
+ public void AddTwoProjectsWithSameGUID()
+ {
+ var solution = CreateSolution();
+ var project1 = solution.AddExistingProject(project1FileName);
+ var project2 = solution.AddExistingProject(project2FileName);
+ Assert.AreEqual(projectGuid, project1.IdGuid);
+ Assert.AreNotEqual(projectGuid, project2.IdGuid);
+ }
}
}
diff --git a/src/Main/SharpDevelop/Project/SolutionFileItem.cs b/src/Main/SharpDevelop/Project/SolutionFileItem.cs
index b9945d64f6..0947336c2c 100644
--- a/src/Main/SharpDevelop/Project/SolutionFileItem.cs
+++ b/src/Main/SharpDevelop/Project/SolutionFileItem.cs
@@ -9,12 +9,11 @@ namespace ICSharpCode.SharpDevelop.Project
class SolutionFileItem : ISolutionFileItem
{
readonly Solution parentSolution;
- readonly Guid idGuid;
public SolutionFileItem(Solution parentSolution)
{
this.parentSolution = parentSolution;
- this.idGuid = Guid.NewGuid();
+ this.IdGuid = Guid.NewGuid();
}
FileName fileName;
@@ -35,9 +34,7 @@ namespace ICSharpCode.SharpDevelop.Project
get { return parentSolution; }
}
- public Guid IdGuid {
- get { return idGuid; }
- }
+ public Guid IdGuid { get; set; }
public Guid TypeGuid {
get { return Guid.Empty; }
diff --git a/src/Main/SharpDevelop/Project/SolutionFolder.cs b/src/Main/SharpDevelop/Project/SolutionFolder.cs
index 3cefe1543b..c915847490 100644
--- a/src/Main/SharpDevelop/Project/SolutionFolder.cs
+++ b/src/Main/SharpDevelop/Project/SolutionFolder.cs
@@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Project
class SolutionFolder : ISolutionFolder
{
readonly Solution parentSolution;
- readonly Guid idGuid;
+ Guid idGuid;
public SolutionFolder(Solution parentSolution, Guid idGuid)
{
@@ -114,6 +114,7 @@ namespace ICSharpCode.SharpDevelop.Project
public Guid IdGuid {
get { return idGuid; }
+ set { idGuid = value; }
}
public Guid TypeGuid {
@@ -135,7 +136,10 @@ namespace ICSharpCode.SharpDevelop.Project
throw new ProjectLoadException("Project " + fileName + " is already part of this solution.");
ProjectLoadInformation loadInfo = new ProjectLoadInformation(parentSolution, fileName, fileName.GetFileNameWithoutExtension());
IProject project = SD.ProjectService.LoadProject(loadInfo);
- Debug.Assert(project.IdGuid != Guid.Empty);
+ if (parentSolution.GetItemByGuid(project.IdGuid) != null) {
+ SD.Log.Warn("Added project has duplicate GUID; a new GUID will be generated.");
+ project.IdGuid = Guid.NewGuid();
+ }
this.Items.Add(project);
project.ProjectLoaded();
ProjectBrowserPad.RefreshViewAsync();
diff --git a/src/Main/SharpDevelop/Project/SolutionLoader.cs b/src/Main/SharpDevelop/Project/SolutionLoader.cs
index 8fb6a39d5f..6f1f96b8fd 100644
--- a/src/Main/SharpDevelop/Project/SolutionLoader.cs
+++ b/src/Main/SharpDevelop/Project/SolutionLoader.cs
@@ -74,6 +74,7 @@ namespace ICSharpCode.SharpDevelop.Project
solutionEntries.Add(information);
if (projectInfoDict.ContainsKey(information.IdGuid)) {
// resolve GUID conflicts
+ SD.Log.WarnFormatted("Detected duplicate GUID in .sln file: {0} is used for {1} and {2}", information.IdGuid, information.ProjectName, projectInfoDict[information.IdGuid].ProjectName);
information.IdGuid = Guid.NewGuid();
fixedGuidConflicts = true;
}
@@ -131,9 +132,11 @@ namespace ICSharpCode.SharpDevelop.Project
// Now that the project configurations have been set, we can actually load the projects:
int projectsLoaded = 0;
foreach (var projectInfo in solutionEntries) {
+ // Make copy of IdGuid just in case the project binding writes to projectInfo.IdGuid
+ Guid idGuid = projectInfo.IdGuid;
ISolutionItem solutionItem;
if (projectInfo.TypeGuid == ProjectTypeGuids.SolutionFolder) {
- solutionItem = solutionFolderDict[projectInfo.IdGuid];
+ solutionItem = solutionFolderDict[idGuid];
} else {
// Load project:
projectInfo.ActiveProjectConfiguration = projectInfo.ConfigurationMapping.GetProjectConfiguration(solution.ActiveConfiguration);
@@ -141,12 +144,29 @@ namespace ICSharpCode.SharpDevelop.Project
using (projectInfo.ProgressMonitor = progress.CreateSubTask(1.0 / projectCount)) {
solutionItem = LoadProjectWithErrorHandling(projectInfo);
}
+ if (solutionItem.IdGuid != idGuid) {
+ Guid projectFileGuid = solutionItem.IdGuid;
+ if (!projectInfoDict.ContainsKey(projectFileGuid)) {
+ // We'll use the GUID from the project file.
+ // Register that GUID in the dictionary to avoid its use by multiple projects.
+ projectInfoDict.Add(projectFileGuid, projectInfo);
+ } else {
+ // Cannot use GUID from project file due to conflict.
+ // To fix the problem without potentially introducing new conflicts in other .sln files that contain the project,
+ // we generate a brand new GUID:
+ solutionItem.IdGuid = Guid.NewGuid();
+ }
+ SD.Log.WarnFormatted(" in project '{0}' is '{1}' and does not match the GUID stored in the solution ({2}). "
+ + "The conflict was resolved using the GUID {3}",
+ projectInfo.ProjectName, projectFileGuid, idGuid, solutionItem.IdGuid);
+ fixedGuidConflicts = true;
+ }
projectsLoaded++;
progress.Progress = (double)projectsLoaded / projectCount;
}
// Add solutionItem to solution:
SolutionFolder folder;
- if (guidToParentFolderDict != null && guidToParentFolderDict.TryGetValue(projectInfo.IdGuid, out folder)) {
+ if (guidToParentFolderDict != null && guidToParentFolderDict.TryGetValue(idGuid, out folder)) {
folder.Items.Add(solutionItem);
} else {
solution.Items.Add(solutionItem);