提交 a7a9dcbe 编写于 作者: M Matt Warren

Add support for document rename

上级 f7ab665b
*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode
Microsoft.CodeAnalysis.ApplyChangesKind.ChangeDocumentInfo = 16 -> Microsoft.CodeAnalysis.ApplyChangesKind
Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption<T>
Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption<T>.CodeStyleOption(T value, Microsoft.CodeAnalysis.CodeStyle.NotificationOption notification) -> void
Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption<T>.Equals(Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption<T> other) -> bool
......@@ -15,6 +16,9 @@ Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Name.set -> void
Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Value.get -> Microsoft.CodeAnalysis.DiagnosticSeverity
Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Value.set -> void
Microsoft.CodeAnalysis.Document.GetOptionsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Options.DocumentOptionSet>
Microsoft.CodeAnalysis.Document.WithFilePath(string filePath) -> Microsoft.CodeAnalysis.Document
Microsoft.CodeAnalysis.Document.WithFolders(System.Collections.Generic.IEnumerable<string> folders) -> Microsoft.CodeAnalysis.Document
Microsoft.CodeAnalysis.Document.WithName(string name) -> Microsoft.CodeAnalysis.Document
Microsoft.CodeAnalysis.DocumentActiveContextChangedEventArgs
Microsoft.CodeAnalysis.DocumentActiveContextChangedEventArgs.DocumentActiveContextChangedEventArgs(Microsoft.CodeAnalysis.Solution solution, Microsoft.CodeAnalysis.Text.SourceTextContainer sourceTextContainer, Microsoft.CodeAnalysis.DocumentId oldActiveContextDocumentId, Microsoft.CodeAnalysis.DocumentId newActiveContextDocumentId) -> void
Microsoft.CodeAnalysis.DocumentActiveContextChangedEventArgs.NewActiveContextDocumentId.get -> Microsoft.CodeAnalysis.DocumentId
......@@ -33,9 +37,13 @@ Microsoft.CodeAnalysis.Options.OptionStorageLocation.OptionStorageLocation() ->
Microsoft.CodeAnalysis.Options.PerLanguageOption<T>.PerLanguageOption(string feature, string name, T defaultValue, params Microsoft.CodeAnalysis.Options.OptionStorageLocation[] storageLocations) -> void
Microsoft.CodeAnalysis.Options.PerLanguageOption<T>.StorageLocations.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Options.OptionStorageLocation>
Microsoft.CodeAnalysis.Solution.Options.get -> Microsoft.CodeAnalysis.Options.OptionSet
Microsoft.CodeAnalysis.Solution.WithDocumentFilePath(Microsoft.CodeAnalysis.DocumentId documentId, string filePath) -> Microsoft.CodeAnalysis.Solution
Microsoft.CodeAnalysis.Solution.WithDocumentName(Microsoft.CodeAnalysis.DocumentId documentId, string name) -> Microsoft.CodeAnalysis.Solution
Microsoft.CodeAnalysis.Tags.WellKnownTags
Microsoft.CodeAnalysis.Workspace.DocumentActiveContextChanged -> System.EventHandler<Microsoft.CodeAnalysis.DocumentActiveContextChangedEventArgs>
Microsoft.CodeAnalysis.Workspace.OnDocumentInfoChanged(Microsoft.CodeAnalysis.DocumentId documentId, Microsoft.CodeAnalysis.DocumentInfo newInfo) -> void
Microsoft.CodeAnalysis.Workspace.RaiseDocumentActiveContextChangedEventAsync(Microsoft.CodeAnalysis.Text.SourceTextContainer sourceTextContainer, Microsoft.CodeAnalysis.DocumentId oldActiveContextDocumentId, Microsoft.CodeAnalysis.DocumentId newActiveContextDocumentId) -> System.Threading.Tasks.Task
Microsoft.CodeAnalysis.WorkspaceChangeKind.DocumentInfoChanged = 17 -> Microsoft.CodeAnalysis.WorkspaceChangeKind
Microsoft.CodeAnalysis.XmlDocumentationProvider
Microsoft.CodeAnalysis.XmlDocumentationProvider.XmlDocumentationProvider() -> void
abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.GetSwitchSections(Microsoft.CodeAnalysis.SyntaxNode switchStatement) -> System.Collections.Generic.IReadOnlyList<Microsoft.CodeAnalysis.SyntaxNode>
......@@ -102,4 +110,5 @@ static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.None -> Micr
static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Suggestion -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption
static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Warning -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption
virtual Microsoft.CodeAnalysis.CodeActions.CodeAction.Tags.get -> System.Collections.Immutable.ImmutableArray<string>
virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode
\ No newline at end of file
virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode
virtual Microsoft.CodeAnalysis.Workspace.ApplyDocumentInfoChanged(Microsoft.CodeAnalysis.DocumentId id, Microsoft.CodeAnalysis.DocumentInfo info) -> void
\ No newline at end of file
......@@ -20,6 +20,7 @@ public enum ApplyChangesKind
RemoveAdditionalDocument = 12,
ChangeAdditionalDocument = 13,
ChangeCompilationOptions = 14,
ChangeParseOptions = 15
ChangeParseOptions = 15,
ChangeDocumentInfo = 16
}
}
......@@ -36,12 +36,32 @@ public partial class Document : TextDocument
/// <summary>
/// The kind of source code this document contains.
/// </summary>
public SourceCodeKind SourceCodeKind
public SourceCodeKind SourceCodeKind => DocumentState.SourceCodeKind;
/// <summary>
/// True if the info of the document change (name, folders, file path; not the content)
/// </summary>
internal bool HasInfoChanged(Document otherDocument)
{
get
{
return DocumentState.SourceCodeKind;
}
return DocumentState.Info != otherDocument.DocumentState.Info
|| DocumentState.SourceCodeKind != otherDocument.SourceCodeKind;
}
/// <summary>
/// Gets a <see cref="DocumentInfo"/> for this document w/o the content.
/// </summary>
internal DocumentInfo GetDocumentInfoWithoutContent()
{
return DocumentState.Info.WithSourceCodeKind(DocumentState.SourceCodeKind);
}
/// <summary>
/// True if the document content has potentially changed.
/// Does not compare actual text.
/// </summary>
internal bool HasContentChanged(Document otherDocument)
{
return DocumentState.HasContentChanged(otherDocument.DocumentState);
}
/// <summary>
......@@ -301,6 +321,31 @@ public Document WithSyntaxRoot(SyntaxNode root)
return this.Project.Solution.WithDocumentSyntaxRoot(this.Id, root, PreservationMode.PreserveIdentity).GetDocument(this.Id);
}
/// <summary>
/// Creates a new instance of this document updated to have the specified name.
/// </summary>
public Document WithName(string name)
{
return this.Project.Solution.WithDocumentName(this.Id, name).GetDocument(this.Id);
}
/// <summary>
/// Creates a new instance of this document updated to have the specified folders.
/// </summary>
public Document WithFolders(IEnumerable<string> folders)
{
return this.Project.Solution.WithDocumentFolders(this.Id, folders).GetDocument(this.Id);
}
/// <summary>
/// Creates a new instance of this document updated to have the specified file path.
/// </summary>
/// <param name="filePath"></param>
public Document WithFilePath(string filePath)
{
return this.Project.Solution.WithDocumentFilePath(this.Id, filePath).GetDocument(this.Id);
}
/// <summary>
/// Get the text changes between this document and a prior version of the same document.
/// The changes, when applied to the text of the old document, will produce the text of the current document.
......
......@@ -288,6 +288,16 @@ private static bool TopLevelChanged(SyntaxTree oldTree, SourceText oldText, Synt
return true;
}
/// <summary>
/// True if the content (text/tree) has changed.
/// </summary>
public bool HasContentChanged(DocumentState oldState)
{
return oldState._treeSource != this._treeSource
|| oldState.sourceTextOpt != this.sourceTextOpt
|| oldState.textAndVersionSource != this.textAndVersionSource;
}
public DocumentState UpdateParseOptions(ParseOptions options)
{
var originalSourceKind = this.SourceCodeKind;
......@@ -337,6 +347,19 @@ public DocumentState UpdateSourceCodeKind(SourceCodeKind kind)
return this.SetParseOptions(this.ParseOptions.WithKind(kind));
}
public DocumentState UpdateName(string name)
{
return new DocumentState(
_languageServices,
this.solutionServices,
this.info.WithName(name),
_options,
this.sourceTextOpt,
this.textAndVersionSource,
_treeSource,
lazyChecksums: null);
}
public DocumentState UpdateFolders(IList<string> folders)
{
return new DocumentState(
......@@ -350,6 +373,19 @@ public DocumentState UpdateFolders(IList<string> folders)
lazyChecksums: null);
}
public DocumentState UpdateFilePath(string filePath)
{
return new DocumentState(
_languageServices,
this.solutionServices,
this.info.WithFilePath(filePath),
_options,
this.sourceTextOpt,
this.textAndVersionSource,
_treeSource,
lazyChecksums: null);
}
public new DocumentState UpdateText(SourceText newText, PreservationMode mode)
{
if (newText == null)
......
......@@ -741,6 +741,20 @@ public Solution RemoveAdditionalDocument(DocumentId documentId)
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the new name.
/// </summary>
public Solution WithDocumentName(DocumentId documentId, string name)
{
var newState = _state.WithDocumentName(documentId, name);
if (newState == _state)
{
return this;
}
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to be contained in
/// the sequence of logical folders.
......@@ -756,6 +770,20 @@ public Solution WithDocumentFolders(DocumentId documentId, IEnumerable<string> f
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the specified file path.
/// </summary>
public Solution WithDocumentFilePath(DocumentId documentId, string filePath)
{
var newState = _state.WithDocumentFilePath(documentId, filePath);
if (newState == _state)
{
return this;
}
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the text
/// specified.
......
......@@ -1090,6 +1090,27 @@ public SolutionState RemoveAdditionalDocument(DocumentId documentId)
return this.ForkProject(newProject);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the specified name.
/// </summary>
public SolutionState WithDocumentName(DocumentId documentId, string name)
{
if (documentId == null)
{
throw new ArgumentNullException(nameof(documentId));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var oldDocument = this.GetDocumentState(documentId);
var newDocument = oldDocument.UpdateName(name);
return this.WithDocumentState(newDocument);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to be contained in
/// the sequence of logical folders.
......@@ -1114,6 +1135,27 @@ public SolutionState WithDocumentFolders(DocumentId documentId, IEnumerable<stri
return this.WithDocumentState(newDocument);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the specified file path.
/// </summary>
public SolutionState WithDocumentFilePath(DocumentId documentId, string filePath)
{
if (documentId == null)
{
throw new ArgumentNullException(nameof(documentId));
}
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
var oldDocument = this.GetDocumentState(documentId);
var newDocument = oldDocument.UpdateFilePath(filePath);
return this.WithDocumentState(newDocument);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the text
/// specified.
......
......@@ -794,6 +794,46 @@ internal void OnHasAllInformationChanged(ProjectId projectId, bool hasAllInforma
}
}
/// <summary>
/// Call this method when the document info changes, such as the name, folders or file path.
/// </summary>
protected internal void OnDocumentInfoChanged(DocumentId documentId, DocumentInfo newInfo)
{
using (_serializationLock.DisposableWait())
{
CheckDocumentIsInCurrentSolution(documentId);
var oldSolution = this.CurrentSolution;
var newSolution = oldSolution;
var oldInfo = oldSolution.GetDocument(documentId).GetDocumentInfoWithoutContent();
if (oldInfo.Name != newInfo.Name)
{
newSolution = newSolution.WithDocumentName(documentId, newInfo.Name);
}
if (oldInfo.Folders != newInfo.Folders)
{
newSolution = newSolution.WithDocumentFolders(documentId, newInfo.Folders);
}
if (oldInfo.FilePath != newInfo.FilePath)
{
newSolution = newSolution.WithDocumentFilePath(documentId, newInfo.FilePath);
}
if (oldInfo.SourceCodeKind != newInfo.SourceCodeKind)
{
newSolution = newSolution.WithDocumentSourceCodeKind(documentId, newInfo.SourceCodeKind);
}
SetCurrentSolution(newSolution);
this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.DocumentInfoChanged, oldSolution, newSolution, documentId: documentId);
}
}
/// <summary>
/// Call this method when the text of a document is updated in the host environment.
/// </summary>
......@@ -1257,25 +1297,41 @@ protected virtual void ApplyProjectChanges(ProjectChanges projectChanges)
{
var oldDoc = projectChanges.OldProject.GetDocument(documentId);
var newDoc = projectChanges.NewProject.GetDocument(documentId);
// see whether we can get oldText
if (!oldDoc.TryGetText(out var oldText))
// update info if changed
if (newDoc.HasInfoChanged(oldDoc))
{
// we can't get old text, there is not much we can do except replacing whole text.
var currentText = newDoc.GetTextAsync(CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); // needs wait
this.ApplyDocumentTextChanged(documentId, currentText);
return;
ApplyDocumentInfoChanged(documentId, newDoc.GetDocumentInfoWithoutContent());
}
// see whether we can get new text
if (!newDoc.TryGetText(out var newText))
// update text if changed
if (newDoc.HasContentChanged(oldDoc))
{
// okay, we have old text, but no new text. let document determine text changes
var textChanges = newDoc.GetTextChangesAsync(oldDoc, CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); // needs wait
this.ApplyDocumentTextChanged(documentId, oldText.WithChanges(textChanges));
return;
}
// What we'd like to do here is figure out what actual text changes occurred and pass them on to the host.
// However, since it is likely that the change was done by replacing the syntax tree, getting the actual text changes is non trivial.
// we have both old and new text, just update using the new text.
this.ApplyDocumentTextChanged(documentId, newText);
if (!oldDoc.TryGetText(out var oldText))
{
// If we don't have easy access to the old text, then either it was never observed or it was kicked out of memory.
// Either way, the new text cannot possibly hold knowledge of the changes, and any new syntax tree will not likely be able to derive them.
// So just use whatever new text we have without preserving text changes.
var currentText = newDoc.GetTextAsync(CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); // needs wait
this.ApplyDocumentTextChanged(documentId, currentText);
}
else if (!newDoc.TryGetText(out var newText))
{
// We have the old text, but no new text is easily available. This typically happens when the content is modified via changes to the syntax tree.
// Ask document to compute equivalent text changes by comparing the syntax trees, and use them to
var textChanges = newDoc.GetTextChangesAsync(oldDoc, CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); // needs wait
this.ApplyDocumentTextChanged(documentId, oldText.WithChanges(textChanges));
}
else
{
// We have both old and new text, so assume the text was changed manually.
// So either the new text already knows the individual changes or we do not have a way to compute them.
this.ApplyDocumentTextChanged(documentId, newText);
}
}
}
[Conditional("DEBUG")]
......@@ -1470,6 +1526,17 @@ protected virtual void ApplyDocumentTextChanged(DocumentId id, SourceText text)
this.OnDocumentTextChanged(id, text, PreservationMode.PreserveValue);
}
/// <summary>
/// This method is called to change the info of a document.
///
/// Override this method to implement the capability of changing a document's info.
/// </summary>
protected virtual void ApplyDocumentInfoChanged(DocumentId id, DocumentInfo info)
{
Debug.Assert(CanApplyChange(ApplyChangesKind.ChangeDocumentInfo));
this.OnDocumentInfoChanged(id, info);
}
/// <summary>
/// This method is called during <see cref="TryApplyChanges(Solution)"/> to add a new additional document to a project.
///
......
......@@ -88,6 +88,11 @@ public enum WorkspaceChangeKind
/// <summary>
/// An additional document in the current solution was changed.
/// </summary>
AdditionalDocumentChanged = 16
AdditionalDocumentChanged = 16,
/// <summary>
/// The document in the current solution had is info changed; name, folders, filepath
/// </summary>
DocumentInfoChanged = 17,
}
}
......@@ -491,5 +491,175 @@ public void SetParseOptions(ProjectId id, ParseOptions options)
base.OnParseOptionsChanged(id, options);
}
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public async Task TestChangeDocumentName_TryApplyChanges()
{
using (var ws = new AdhocWorkspace())
{
var projectId = ws.AddProject("TestProject", LanguageNames.CSharp).Id;
var originalDoc = ws.AddDocument(projectId, "TestDocument", SourceText.From(""));
Assert.Equal(originalDoc.Name, "TestDocument");
var newName = "ChangedName";
var changedDoc = originalDoc.WithName(newName);
Assert.Equal(newName, changedDoc.Name);
var tcs = new TaskCompletionSource<bool>();
ws.WorkspaceChanged += (s, args) =>
{
if (args.Kind == WorkspaceChangeKind.DocumentInfoChanged
&& args.DocumentId == originalDoc.Id)
{
tcs.SetResult(true);
}
};
Assert.True(ws.TryApplyChanges(changedDoc.Project.Solution));
var appliedDoc = ws.CurrentSolution.GetDocument(originalDoc.Id);
Assert.Equal(newName, appliedDoc.Name);
await Task.WhenAny(tcs.Task, Task.Delay(1000));
Assert.True(tcs.Task.IsCompleted && tcs.Task.Result);
}
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public async Task TestChangeDocumentFolders_TryApplyChanges()
{
using (var ws = new AdhocWorkspace())
{
var projectId = ws.AddProject("TestProject", LanguageNames.CSharp).Id;
var originalDoc = ws.AddDocument(projectId, "TestDocument", SourceText.From(""));
Assert.Equal(0, originalDoc.Folders.Count);
var changedDoc = originalDoc.WithFolders(new[] { "A", "B" });
Assert.Equal(2, changedDoc.Folders.Count);
Assert.Equal("A", changedDoc.Folders[0]);
Assert.Equal("B", changedDoc.Folders[1]);
var tcs = new TaskCompletionSource<bool>();
ws.WorkspaceChanged += (s, args) =>
{
if (args.Kind == WorkspaceChangeKind.DocumentInfoChanged
&& args.DocumentId == originalDoc.Id)
{
tcs.SetResult(true);
}
};
Assert.True(ws.TryApplyChanges(changedDoc.Project.Solution));
var appliedDoc = ws.CurrentSolution.GetDocument(originalDoc.Id);
Assert.Equal(2, appliedDoc.Folders.Count);
Assert.Equal("A", appliedDoc.Folders[0]);
Assert.Equal("B", appliedDoc.Folders[1]);
await Task.WhenAny(tcs.Task, Task.Delay(1000));
Assert.True(tcs.Task.IsCompleted && tcs.Task.Result);
}
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public async Task TestChangeDocumentFilePath_TryApplyChanges()
{
using (var ws = new AdhocWorkspace())
{
var projectId = ws.AddProject("TestProject", LanguageNames.CSharp).Id;
var originalDoc = ws.AddDocument(projectId, "TestDocument", SourceText.From(""));
Assert.Null(originalDoc.FilePath);
var newPath = @"\foo\TestDocument.cs";
var changedDoc = originalDoc.WithFilePath(newPath);
Assert.Equal(newPath, changedDoc.FilePath);
var tcs = new TaskCompletionSource<bool>();
ws.WorkspaceChanged += (s, args) =>
{
if (args.Kind == WorkspaceChangeKind.DocumentInfoChanged
&& args.DocumentId == originalDoc.Id)
{
tcs.SetResult(true);
}
};
Assert.True(ws.TryApplyChanges(changedDoc.Project.Solution));
var appliedDoc = ws.CurrentSolution.GetDocument(originalDoc.Id);
Assert.Equal(newPath, appliedDoc.FilePath);
await Task.WhenAny(tcs.Task, Task.Delay(1000));
Assert.True(tcs.Task.IsCompleted && tcs.Task.Result);
}
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public async Task TestChangeDocumentSourceCodeKind_TryApplyChanges()
{
using (var ws = new AdhocWorkspace())
{
var projectId = ws.AddProject("TestProject", LanguageNames.CSharp).Id;
var originalDoc = ws.AddDocument(projectId, "TestDocument", SourceText.From(""));
Assert.Equal(SourceCodeKind.Regular, originalDoc.SourceCodeKind);
var changedDoc = originalDoc.WithSourceCodeKind(SourceCodeKind.Script);
Assert.Equal(SourceCodeKind.Script, changedDoc.SourceCodeKind);
var tcs = new TaskCompletionSource<bool>();
ws.WorkspaceChanged += (s, args) =>
{
if (args.Kind == WorkspaceChangeKind.DocumentInfoChanged
&& args.DocumentId == originalDoc.Id)
{
tcs.SetResult(true);
}
};
Assert.True(ws.TryApplyChanges(changedDoc.Project.Solution));
var appliedDoc = ws.CurrentSolution.GetDocument(originalDoc.Id);
Assert.Equal(SourceCodeKind.Script, appliedDoc.SourceCodeKind);
await Task.WhenAny(tcs.Task, Task.Delay(1000));
Assert.True(tcs.Task.IsCompleted && tcs.Task.Result);
}
}
[Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
public void TestChangeDocumentInfo_TryApplyChanges()
{
using (var ws = new AdhocWorkspace())
{
var projectId = ws.AddProject("TestProject", LanguageNames.CSharp).Id;
var originalDoc = ws.AddDocument(projectId, "TestDocument", SourceText.From(""));
Assert.Equal(originalDoc.Name, "TestDocument");
Assert.Equal(0, originalDoc.Folders.Count);
Assert.Null(originalDoc.FilePath);
var newName = "ChangedName";
var newPath = @"\A\B\ChangedName.cs";
var changedDoc = originalDoc.WithName(newName).WithFolders(new[] { "A", "B" }).WithFilePath(newPath);
Assert.Equal(newName, changedDoc.Name);
Assert.Equal(2, changedDoc.Folders.Count);
Assert.Equal("A", changedDoc.Folders[0]);
Assert.Equal("B", changedDoc.Folders[1]);
Assert.Equal(newPath, changedDoc.FilePath);
Assert.True(ws.TryApplyChanges(changedDoc.Project.Solution));
var appliedDoc = ws.CurrentSolution.GetDocument(originalDoc.Id);
Assert.Equal(newName, appliedDoc.Name);
Assert.Equal(2, appliedDoc.Folders.Count);
Assert.Equal("A", appliedDoc.Folders[0]);
Assert.Equal("B", appliedDoc.Folders[1]);
Assert.Equal(newPath, appliedDoc.FilePath);
}
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册