提交 02896b58 编写于 作者: T TIHan

Initial commit for project document ordering

上级 209eb568
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -57,5 +58,350 @@ public void AddRemoveAdditionalFile_CPS()
Assert.Empty(GetCurrentAdditionalDocuments());
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFiles_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
project.RemoveSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath2);
project.RemoveSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath4);
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath2, sourceFileFullPath3, sourceFileFullPath4, sourceFileFullPath5 });
var documents = GetCurrentDocuments().ToArray();
Assert.Equal(documents[0].FilePath, sourceFileFullPath1, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[1].FilePath, sourceFileFullPath2, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[2].FilePath, sourceFileFullPath3, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[3].FilePath, sourceFileFullPath4, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[4].FilePath, sourceFileFullPath5, StringComparer.OrdinalIgnoreCase);
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesBatch_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
// Add a file outside the batch.
project.AddSourceFile(sourceFileFullPath2);
project.StartBatch();
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
// Removing path2 to test removal of a file the actual internal project state has changed outside of the batch.
project.RemoveSourceFile(sourceFileFullPath2);
// Removing path4 to test remove of a file when it was also added in a batch.
project.RemoveSourceFile(sourceFileFullPath4);
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath3, sourceFileFullPath5 });
project.EndBatch();
var documents = GetCurrentDocuments().ToArray();
Assert.Equal(documents[0].FilePath, sourceFileFullPath1, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[1].FilePath, sourceFileFullPath3, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[2].FilePath, sourceFileFullPath5, StringComparer.OrdinalIgnoreCase);
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesBatchWithReAdding_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
// Add a file outside the batch.
project.AddSourceFile(sourceFileFullPath2);
project.StartBatch();
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
// Removing path2 to test removal of a file the actual internal project state has changed outside of the batch.
project.RemoveSourceFile(sourceFileFullPath2);
// Removing path4 to test remove of a file when it was also added in a batch.
project.RemoveSourceFile(sourceFileFullPath4);
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath3, sourceFileFullPath5 });
// Re-adding / re-removing / re-adding again.
project.AddSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath4);
project.RemoveSourceFile(sourceFileFullPath2);
project.RemoveSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath4);
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath2, sourceFileFullPath3, sourceFileFullPath4, sourceFileFullPath5 });
project.EndBatch();
var documents = GetCurrentDocuments().ToArray();
Assert.Equal(documents[0].FilePath, sourceFileFullPath1, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[1].FilePath, sourceFileFullPath2, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[2].FilePath, sourceFileFullPath3, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[3].FilePath, sourceFileFullPath4, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[4].FilePath, sourceFileFullPath5, StringComparer.OrdinalIgnoreCase);
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesBatchAddAfterReorder_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
project.StartBatch();
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath2);
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath2 });
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
project.EndBatch();
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath2, sourceFileFullPath3, sourceFileFullPath4, sourceFileFullPath5 });
var documents = GetCurrentDocuments().ToArray();
Assert.Equal(documents[0].FilePath, sourceFileFullPath1, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[1].FilePath, sourceFileFullPath2, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[2].FilePath, sourceFileFullPath3, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[3].FilePath, sourceFileFullPath4, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[4].FilePath, sourceFileFullPath5, StringComparer.OrdinalIgnoreCase);
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesBatchRemoveAfterReorder_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
project.StartBatch();
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath2, sourceFileFullPath3, sourceFileFullPath4, sourceFileFullPath5 });
project.RemoveSourceFile(sourceFileFullPath3);
project.RemoveSourceFile(sourceFileFullPath4);
project.RemoveSourceFile(sourceFileFullPath5);
project.EndBatch();
project.ReorderSourceFiles(new[] { sourceFileFullPath1, sourceFileFullPath2 });
var documents = GetCurrentDocuments().ToArray();
Assert.Equal(documents[0].FilePath, sourceFileFullPath1, StringComparer.OrdinalIgnoreCase);
Assert.Equal(documents[1].FilePath, sourceFileFullPath2, StringComparer.OrdinalIgnoreCase);
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesExceptions_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
project.RemoveSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath2);
project.RemoveSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath4);
// This should throw due to not passing all of the files.
Assert.Throws<ArgumentException>(() => project.ReorderSourceFiles(new[] { sourceFileFullPath4, sourceFileFullPath5 }));
// This should throw because the path does not exist in the project.
Assert.Throws<InvalidOperationException>(() => project.ReorderSourceFiles(new[] { @"C:\invalid source file", sourceFileFullPath2, sourceFileFullPath3, sourceFileFullPath4, sourceFileFullPath5 }));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(new List<string>()));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(null));
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesBatchExceptions_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
var sourceFileFullPath3 = @"c:\source3.cs";
var sourceFileFullPath4 = @"c:\source4.cs";
var sourceFileFullPath5 = @"c:\source5.cs";
project.StartBatch();
Assert.Throws<ArgumentException>(() => project.ReorderSourceFiles(new[] { sourceFileFullPath4, sourceFileFullPath5 }));
Assert.Throws<ArgumentException>(() => project.ReorderSourceFiles(new[] { @"C:\invalid source file" })); // no files were added, therefore we should get an argument exception
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(new List<string>()));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(null));
project.AddSourceFile(sourceFileFullPath1);
// Test before we add/remove the rest of source files in the batch.
Assert.Throws<ArgumentException>(() => project.ReorderSourceFiles(new[] { sourceFileFullPath4, sourceFileFullPath5 }));
Assert.Throws<InvalidOperationException>(() => project.ReorderSourceFiles(new[] { @"C:\invalid source file" }));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(new List<string>()));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(null));
project.AddSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath3);
project.AddSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath5);
project.RemoveSourceFile(sourceFileFullPath2);
project.AddSourceFile(sourceFileFullPath2);
project.RemoveSourceFile(sourceFileFullPath4);
project.AddSourceFile(sourceFileFullPath4);
Assert.Throws<ArgumentException>(() => project.ReorderSourceFiles(new[] { sourceFileFullPath4, sourceFileFullPath5 }));
Assert.Throws<InvalidOperationException>(() => project.ReorderSourceFiles(new[] { @"C:\invalid source file", sourceFileFullPath2, sourceFileFullPath3, sourceFileFullPath4, sourceFileFullPath5 }));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(new List<string>()));
Assert.Throws<ArgumentOutOfRangeException>(() => project.ReorderSourceFiles(null));
project.EndBatch();
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void ReorderSourceFilesBatchExceptionRemoveFile_CPS()
{
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
var sourceFileFullPath1 = @"c:\source1.cs";
var sourceFileFullPath2 = @"c:\source2.cs";
project.AddSourceFile(sourceFileFullPath1);
project.AddSourceFile(sourceFileFullPath2);
project.StartBatch();
project.RemoveSourceFile(sourceFileFullPath2);
Assert.Throws<InvalidOperationException>(() => project.ReorderSourceFiles(new[] { sourceFileFullPath2 }));
project.EndBatch();
var documents = GetCurrentDocuments().ToArray();
Assert.Equal(documents[0].FilePath, sourceFileFullPath1, StringComparer.OrdinalIgnoreCase);
}
}
}
}
......@@ -45,5 +45,7 @@ internal interface IWorkspaceProjectContext : IDisposable
void StartBatch();
void EndBatch();
void ReorderSourceFiles(IEnumerable<string> filePaths);
}
}
......@@ -1017,6 +1017,11 @@ public void RemoveOutputPath(string outputPath)
_workspace.RemoveProjectOutputPath(Id, outputPath);
}
public void ReorderSourceFiles(ImmutableArray<string> filePaths)
{
_sourceFiles.ReorderFiles(filePaths);
}
/// <summary>
/// Clears a list and zeros out the capacity. The lists we use for batching are likely to get large during an initial load, but after
/// that point should never get that large again.
......@@ -1074,6 +1079,11 @@ private sealed class BatchingDocumentCollection
/// </summary>
private readonly List<DocumentId> _documentsRemovedInBatch = new List<DocumentId>();
/// <summary>
/// The current
/// </summary>
private Nullable<ImmutableArray<string>> _orderedFilesInBatch = null;
private readonly Func<Solution, DocumentId, bool> _documentAlreadyInWorkspace;
private readonly Action<Workspace, DocumentInfo> _documentAddAction;
private readonly Action<Workspace, DocumentId> _documentRemoveAction;
......@@ -1114,6 +1124,9 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta
throw new ArgumentException($"'{fullPath}' has already been added to this project.", nameof(fullPath));
}
// Adding a file messes up the ordering and does not gaurantee any kind of order, so invalidate any ordered files in batch.
_orderedFilesInBatch = null;
_documentPathsToDocumentIds.Add(fullPath, documentId);
_project._documentFileWatchingTokens.Add(documentId, _project._documentFileChangeContext.EnqueueWatchingFile(fullPath));
......@@ -1200,6 +1213,9 @@ public void AddDynamicFile(IDynamicFileInfoProvider fileInfoProvider, DynamicFil
throw new ArgumentException($"'{filePath}' has already been added to this project.", nameof(filePath));
}
// Adding a dynamic file messes up the ordering and does not gaurantee any kind of order, so invalidate any ordered files in batch.
_orderedFilesInBatch = null;
_documentPathsToDocumentIds.Add(filePath, documentId);
_documentIdToDynamicFileInfoProvider.Add(documentId, fileInfoProvider);
......@@ -1268,6 +1284,9 @@ public void RemoveFile(string fullPath)
private void RemoveFileInternal(DocumentId documentId, string fullPath)
{
// Removing a file messes up the ordering and does not gaurantee any kind of order, so invalidate any ordered files in batch.
_orderedFilesInBatch = null;
_documentPathsToDocumentIds.Remove(fullPath);
// There are two cases:
......@@ -1440,6 +1459,39 @@ public void ProcessFileChange(string projectSystemFilePath, string workspaceFile
}
}
public void ReorderFiles(ImmutableArray<string> filePaths)
{
if (filePaths.IsEmpty)
{
throw new ArgumentOutOfRangeException("The specified files are empty.", nameof(filePaths));
}
lock (_project._gate)
{
if (_documentPathsToDocumentIds.Count != filePaths.Length)
{
throw new ArgumentException("The specified files do not equal the project document count.", nameof(filePaths));
}
foreach (var filePath in filePaths)
{
if (!_documentPathsToDocumentIds.ContainsKey(filePath))
{
throw new InvalidOperationException($"The file '{filePath}' does not exist in the project.");
}
}
if (_project._activeBatchScopes > 0)
{
_orderedFilesInBatch = filePaths;
}
else
{
_project._workspace.ApplyBatchChangeToProject(_project.Id, oldSolution => UpdateProjectDocumentsOrder(oldSolution, filePaths));
}
}
}
internal Solution UpdateSolutionForBatch(
Solution solution,
ImmutableArray<string>.Builder documentFileNamesAdded,
......@@ -1470,9 +1522,36 @@ public void ProcessFileChange(string projectSystemFilePath, string workspaceFile
ClearAndZeroCapacity(_documentsRemovedInBatch);
// Update project's order of documents.
solution = UpdateProjectDocumentsOrderForBatch(solution);
return solution;
}
internal Solution UpdateProjectDocumentsOrderForBatch(Solution solution)
{
// The action of ordering documents was not included in the batch. Simply return the solution.
if (_orderedFilesInBatch == null)
{
return solution;
}
var orderedFiles = _orderedFilesInBatch;
_orderedFilesInBatch = null;
return UpdateProjectDocumentsOrder(solution, orderedFiles);
}
private Solution UpdateProjectDocumentsOrder(Solution solution, IEnumerable<string> filePaths)
{
var projectId = _project.Id;
var documentIds =
filePaths.Select(x => solution.GetDocumentIdsWithFilePath(x).Single(id => id.ProjectId == projectId));
return solution.WithProjectDocumentsOrder(projectId, documentIds.ToImmutableList());
}
private DocumentInfo CreateDocumentInfoFromFileInfo(DynamicFileInfo fileInfo, IEnumerable<string> folders)
{
// we use this file path for editorconfig.
......
......@@ -227,6 +227,11 @@ public void EndBatch()
scope.Dispose();
}
public void ReorderSourceFiles(IEnumerable<string> filePaths)
{
_visualStudioProject.ReorderSourceFiles(filePaths.ToImmutableArrayOrEmpty());
}
internal VisualStudioProject GetProject_TestOnly()
{
return _visualStudioProject;
......
Microsoft.CodeAnalysis.Solution.WithProjectDocumentsOrder(Microsoft.CodeAnalysis.ProjectId projectId, System.Collections.Immutable.ImmutableList<Microsoft.CodeAnalysis.DocumentId> documentIds) -> Microsoft.CodeAnalysis.Solution
abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AliasImportDeclaration(string aliasIdentifierName, Microsoft.CodeAnalysis.SyntaxNode name) -> Microsoft.CodeAnalysis.SyntaxNode
abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.NameExpression(Microsoft.CodeAnalysis.INamespaceOrTypeSymbol namespaceOrTypeSymbol) -> Microsoft.CodeAnalysis.SyntaxNode
const Microsoft.CodeAnalysis.Classification.ClassificationTypeNames.RegexAlternation = "regex - alternation" -> string
......
......@@ -671,6 +671,35 @@ public ProjectState UpdateAdditionalDocument(TextDocumentState newDocument, bool
latestDocumentTopLevelChangeVersion: dependentSemanticVersion);
}
public ProjectState UpdateDocumentsOrder(ImmutableList<DocumentId> documentIds)
{
if (documentIds == null)
{
throw new ArgumentNullException(nameof(documentIds));
}
if (documentIds.IsEmpty)
{
throw new ArgumentOutOfRangeException("The specified documents are empty.", nameof(documentIds));
}
if (documentIds.Count != _documentIds.Count)
{
throw new ArgumentException($"The specified documents do not equal the project document count.", nameof(documentIds));
}
foreach (var documentId in documentIds)
{
if (!this.ContainsDocument(documentId))
{
throw new InvalidOperationException($"The document '{documentId}' does not exist in the project.");
}
}
return this.With(
documentIds: documentIds);
}
private void GetLatestDependentVersions(
IImmutableDictionary<DocumentId, DocumentState> newDocumentStates,
IImmutableDictionary<DocumentId, TextDocumentState> newAdditionalDocumentStates,
......
......@@ -469,6 +469,21 @@ public Solution WithProjectReferences(ProjectId projectId, IEnumerable<ProjectRe
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance with the project documents in the order by the specified document ids.
/// The specified document ids must be the same as what is already in the project; no adding or removing is allowed.
/// </summary>
public Solution WithProjectDocumentsOrder(ProjectId projectId, ImmutableList<DocumentId> documentIds)
{
var newState = _state.WithProjectDocumentsOrder(projectId, documentIds);
if (newState == _state)
{
return this;
}
return new Solution(newState);
}
/// <summary>
/// Create a new solution instance with the project specified updated to include the
/// specified metadata reference.
......
......@@ -925,6 +925,30 @@ public SolutionState WithProjectReferences(ProjectId projectId, IEnumerable<Proj
return this.ForkProject(newProject, newDependencyGraph: _dependencyGraph.WithProjectReferences(projectId, projectReferences.Select(p => p.ProjectId)));
}
/// <summary>
/// Creates a new solution instance with the project documents in the order by the specified document ids.
/// The specified document ids must be the same as what is already in the project; no adding or removing is allowed.
/// </summary>
public SolutionState WithProjectDocumentsOrder(ProjectId projectId, ImmutableList<DocumentId> documentIds)
{
if (projectId == null)
{
throw new ArgumentNullException(nameof(projectId));
}
if (documentIds == null)
{
throw new ArgumentNullException(nameof(documentIds));
}
CheckContainsProject(projectId);
var oldProject = this.GetProjectState(projectId);
var newProject = oldProject.UpdateDocumentsOrder(documentIds);
return this.ForkProject(newProject);
}
/// <summary>
/// Create a new solution instance with the project specified updated to include the
/// specified metadata reference.
......
......@@ -1520,6 +1520,83 @@ public void TestWithSyntaxTree()
var root = newTree.GetRoot();
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public void TestUpdateDocumentsOrder()
{
var solution = CreateSolution();
var pid = ProjectId.CreateNewId();
Func<ImmutableArray<Document>> GetDocuments = () => solution.GetProject(pid).Documents.ToImmutableArray();
solution = solution.AddProject(pid, "test", "test.dll", LanguageNames.CSharp);
var text1 = "public class Test1 {}";
var did1 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did1, "test1.cs", text1);
var text2 = "public class Test2 {}";
var did2 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did2, "test2.cs", text2);
var text3 = "public class Test3 {}";
var did3 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did3, "test3.cs", text3);
var text4 = "public class Test4 {}";
var did4 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did4, "test4.cs", text4);
var text5 = "public class Test5 {}";
var did5 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did5, "test5.cs", text5);
solution = solution.WithProjectDocumentsOrder(pid, ImmutableList.CreateRange(new[] { did5, did4, did3, did2, did1 }));
var documents = GetDocuments();
Assert.Equal("test5.cs", documents[0].Name, StringComparer.OrdinalIgnoreCase);
Assert.Equal("test4.cs", documents[1].Name, StringComparer.OrdinalIgnoreCase);
Assert.Equal("test3.cs", documents[2].Name, StringComparer.OrdinalIgnoreCase);
Assert.Equal("test2.cs", documents[3].Name, StringComparer.OrdinalIgnoreCase);
Assert.Equal("test1.cs", documents[4].Name, StringComparer.OrdinalIgnoreCase);
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public void TestUpdateDocumentsOrderExceptions()
{
var solution = CreateSolution();
var pid = ProjectId.CreateNewId();
solution = solution.AddProject(pid, "test", "test.dll", LanguageNames.CSharp);
var text1 = "public class Test1 {}";
var did1 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did1, "test1.cs", text1);
var text2 = "public class Test2 {}";
var did2 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did2, "test2.cs", text2);
var text3 = "public class Test3 {}";
var did3 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did3, "test3.cs", text3);
var text4 = "public class Test4 {}";
var did4 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did4, "test4.cs", text4);
var text5 = "public class Test5 {}";
var did5 = DocumentId.CreateNewId(pid);
solution = solution.AddDocument(did5, "test5.cs", text5);
solution = solution.RemoveDocument(did5);
Assert.Throws<ArgumentOutOfRangeException>(() => solution = solution.WithProjectDocumentsOrder(pid, ImmutableList.Create<DocumentId>()));
Assert.Throws<ArgumentNullException>(() => solution = solution.WithProjectDocumentsOrder(pid, null));
Assert.Throws<InvalidOperationException>(() => solution = solution.WithProjectDocumentsOrder(pid, ImmutableList.CreateRange(new[] { did5, did3, did2, did1 })));
Assert.Throws<ArgumentException>(() => solution = solution.WithProjectDocumentsOrder(pid, ImmutableList.CreateRange(new[] { did3, did2, did1 })));
}
private static void GetMultipleProjects(
out Project csBrokenProject,
out Project vbNormalProject,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册