Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WithDocumentAttributes #74421

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -387,21 +387,15 @@ public DocumentState UpdateSourceCodeKind(SourceCodeKind kind)
return UpdateParseOptionsAndSourceCodeKind(ParseOptions.WithKind(kind), onlyPreprocessorDirectiveChange: false);
}

return UpdateAttributes(Attributes.With(sourceCodeKind: kind));
return WithAttributes(Attributes.With(sourceCodeKind: kind));
}

public DocumentState UpdateName(string name)
=> UpdateAttributes(Attributes.With(name: name));

public DocumentState UpdateFilePath(string? path)
=> UpdateAttributes(Attributes.With(filePath: path));

public DocumentState UpdateFolders(IReadOnlyList<string> folders)
=> UpdateAttributes(Attributes.With(folders: folders));

private DocumentState UpdateAttributes(DocumentInfo.DocumentAttributes newAttributes)
public DocumentState WithAttributes(DocumentInfo.DocumentAttributes newAttributes)
{
Debug.Assert(newAttributes != Attributes);
if (ReferenceEquals(newAttributes, Attributes))
{
return this;
}

ITreeAndVersionSource? newTreeSource;

Expand Down
16 changes: 13 additions & 3 deletions src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,10 @@ public Solution WithDocumentName(DocumentId documentId, string name)
throw new ArgumentNullException(nameof(name));
}

return WithCompilationState(_compilationState.WithDocumentName(documentId, name));
return WithCompilationState(_compilationState.WithDocumentAttributes(
documentId,
name,
static (attributes, value) => attributes.With(name: value)));
}

/// <summary>
Expand All @@ -1163,7 +1166,10 @@ public Solution WithDocumentFolders(DocumentId documentId, IEnumerable<string>?

var collection = PublicContract.ToBoxedImmutableArrayWithNonNullItems(folders, nameof(folders));

return WithCompilationState(_compilationState.WithDocumentFolders(documentId, collection));
return WithCompilationState(_compilationState.WithDocumentAttributes(
documentId,
collection,
static (attributes, value) => attributes.With(folders: value)));
}

/// <summary>
Expand All @@ -1172,7 +1178,11 @@ public Solution WithDocumentFolders(DocumentId documentId, IEnumerable<string>?
public Solution WithDocumentFilePath(DocumentId documentId, string? filePath)
{
CheckContainsDocument(documentId);
return WithCompilationState(_compilationState.WithDocumentFilePath(documentId, filePath));

return WithCompilationState(_compilationState.WithDocumentAttributes(
documentId,
filePath,
static (attributes, value) => attributes.With(filePath: value)));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -688,72 +688,60 @@ public SolutionCompilationState WithProjectAnalyzerReferences(
forkTracker: true);
}

/// <inheritdoc cref="SolutionState.WithDocumentName"/>
public SolutionCompilationState WithDocumentName(
DocumentId documentId, string name)
{
return UpdateDocumentState(
this.SolutionState.WithDocumentName(documentId, name), documentId);
}

/// <inheritdoc cref="SolutionState.WithDocumentFolders"/>
public SolutionCompilationState WithDocumentFolders(
DocumentId documentId, IReadOnlyList<string> folders)
{
return UpdateDocumentState(
this.SolutionState.WithDocumentFolders(documentId, folders), documentId);
}

/// <inheritdoc cref="SolutionState.WithDocumentFilePath"/>
public SolutionCompilationState WithDocumentFilePath(
DocumentId documentId, string? filePath)
/// <inheritdoc cref="SolutionState.WithDocumentAttributes{TValue}"/>
public SolutionCompilationState WithDocumentAttributes<TArg>(
DocumentId documentId,
TArg arg,
Func<DocumentInfo.DocumentAttributes, TArg, DocumentInfo.DocumentAttributes> updateAttributes)
{
return UpdateDocumentState(
this.SolutionState.WithDocumentFilePath(documentId, filePath), documentId);
SolutionState.WithDocumentAttributes(documentId, arg, updateAttributes), documentId);
}

internal SolutionCompilationState WithDocumentTexts(ImmutableArray<(DocumentId documentId, SourceText text)> texts, PreservationMode mode)
=> WithDocumentContents(
texts, SourceTextIsUnchanged,
static (documentState, text, mode) => documentState.UpdateText(text, mode),
data: mode);
=> UpdateDocumentsInMultipleProjects(
texts,
arg: mode,
updateDocument: static (oldDocumentState, text, mode) =>
SourceTextIsUnchanged(oldDocumentState, text) ? oldDocumentState : oldDocumentState.UpdateText(text, mode));

private static bool SourceTextIsUnchanged(DocumentState oldDocument, SourceText text, PreservationMode mode)
private static bool SourceTextIsUnchanged(DocumentState oldDocument, SourceText text)
=> oldDocument.TryGetText(out var oldText) && text == oldText;

private SolutionCompilationState WithDocumentContents<TContent, TData>(
ImmutableArray<(DocumentId documentId, TContent content)> texts,
Func<DocumentState, TContent, TData, bool> isUnchanged,
Func<DocumentState, TContent, TData, DocumentState> updateContent,
TData data)
private SolutionCompilationState UpdateDocumentsInMultipleProjects<TDocumentData, TArg>(
ImmutableArray<(DocumentId documentId, TDocumentData documentData)> updates,
TArg arg,
Func<DocumentState, TDocumentData, TArg, DocumentState> updateDocument)
{
return UpdateDocumentsInMultipleProjects(
texts.GroupBy(d => d.documentId.ProjectId).Select(g =>
{
var projectId = g.Key;
var projectState = this.SolutionState.GetRequiredProjectState(projectId);

using var _ = ArrayBuilder<DocumentState>.GetInstance(out var newDocumentStates);
foreach (var (documentId, content) in g)
{
var documentState = projectState.DocumentStates.GetRequiredState(documentId);
if (isUnchanged(documentState, content, data))
continue;

newDocumentStates.Add(updateContent(documentState, content, data));
}

return (projectId, newDocumentStates.ToImmutableAndClear());
}),
static (projectState, newDocumentStates) =>
{
return new TranslationAction.TouchDocumentsAction(
projectState,
projectState.UpdateDocuments(newDocumentStates, contentChanged: true),
newDocumentStates);
});
updates.GroupBy(static d => d.documentId.ProjectId).Select(g =>
{
var projectId = g.Key;
var projectState = SolutionState.GetRequiredProjectState(projectId);

using var _ = ArrayBuilder<DocumentState>.GetInstance(out var newDocumentStates);
foreach (var (documentId, documentData) in g)
{
var documentState = projectState.DocumentStates.GetRequiredState(documentId);
var newDocumentState = updateDocument(documentState, documentData, arg);

if (ReferenceEquals(documentState, newDocumentState))
continue;

newDocumentStates.Add(newDocumentState);
}

return (projectId, newDocumentStates.ToImmutableAndClear());
}),
GetUpdateDocumentsTranslationAction);
}

private static TranslationAction GetUpdateDocumentsTranslationAction(ProjectState projectState, ImmutableArray<DocumentState> newDocumentStates)
=> new TranslationAction.TouchDocumentsAction(
projectState,
projectState.UpdateDocuments(newDocumentStates, contentChanged: true),
newDocumentStates);

public SolutionCompilationState WithDocumentState(
DocumentState documentState)
{
Expand Down Expand Up @@ -807,32 +795,25 @@ public SolutionCompilationState WithAnalyzerConfigDocumentText(
/// <inheritdoc cref="Solution.WithDocumentSyntaxRoots(ImmutableArray{ValueTuple{DocumentId, SyntaxNode}}, PreservationMode)"/>
public SolutionCompilationState WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root)> syntaxRoots, PreservationMode mode)
{
return WithDocumentContents(
syntaxRoots, IsUnchanged,
static (documentState, root, mode) => documentState.UpdateTree(root, mode),
data: mode);

static bool IsUnchanged(DocumentState oldDocument, SyntaxNode root, PreservationMode _)
{
return oldDocument.TryGetSyntaxTree(out var oldTree) &&
oldTree.TryGetRoot(out var oldRoot) &&
oldRoot == root;
}
return UpdateDocumentsInMultipleProjects(
syntaxRoots,
arg: mode,
static (oldDocumentState, root, mode) =>
oldDocumentState.TryGetSyntaxTree(out var oldTree) && oldTree.TryGetRoot(out var oldRoot) && oldRoot == root
? oldDocumentState
: oldDocumentState.UpdateTree(root, mode));
}

public SolutionCompilationState WithDocumentContentsFrom(
ImmutableArray<(DocumentId documentId, DocumentState documentState)> documentIdsAndStates, bool forceEvenIfTreesWouldDiffer)
{
return WithDocumentContents(
return UpdateDocumentsInMultipleProjects(
documentIdsAndStates,
isUnchanged: static (oldDocumentState, documentState, forceEvenIfTreesWouldDiffer) =>
{
return oldDocumentState.TextAndVersionSource == documentState.TextAndVersionSource
&& oldDocumentState.TreeSource == documentState.TreeSource;
},
arg: forceEvenIfTreesWouldDiffer,
static (oldDocumentState, documentState, forceEvenIfTreesWouldDiffer) =>
oldDocumentState.UpdateTextAndTreeContents(documentState.TextAndVersionSource, documentState.TreeSource, forceEvenIfTreesWouldDiffer),
data: forceEvenIfTreesWouldDiffer);
oldDocumentState.TextAndVersionSource == documentState.TextAndVersionSource && oldDocumentState.TreeSource == documentState.TreeSource
? oldDocumentState
: oldDocumentState.UpdateTextAndTreeContents(documentState.TextAndVersionSource, documentState.TreeSource, forceEvenIfTreesWouldDiffer));
}

/// <inheritdoc cref="SolutionState.WithDocumentSourceCodeKind"/>
Expand Down Expand Up @@ -1696,7 +1677,7 @@ public SolutionCompilationState WithDocumentText(IEnumerable<DocumentId?> docume
// the same text (for example, when GetOpenDocumentInCurrentContextWithChanges) is called.
//
// The use of GetRequiredState mirrors what happens in WithDocumentTexts
if (!SourceTextIsUnchanged(documentState, text, mode))
if (!SourceTextIsUnchanged(documentState, text))
changedDocuments.Add((documentId, text));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -944,49 +944,23 @@ public SolutionState WithFallbackAnalyzerOptions(ImmutableDictionary<string, Str
}

/// <summary>
/// Creates a new solution instance with the document specified updated to have the specified name.
/// Creates a new solution instance with an attribute of the document updated, if its value has changed.
/// </summary>
public StateChange WithDocumentName(DocumentId documentId, string name)
public StateChange WithDocumentAttributes<TArg>(
DocumentId documentId,
TArg arg,
Func<DocumentInfo.DocumentAttributes, TArg, DocumentInfo.DocumentAttributes> updateAttributes)
{
var oldDocument = GetRequiredDocumentState(documentId);
if (oldDocument.Attributes.Name == name)
{
var oldProject = GetRequiredProjectState(documentId.ProjectId);
return new(this, oldProject, oldProject);
}

return UpdateDocumentState(oldDocument.UpdateName(name), contentChanged: false);
}

/// <summary>
/// Creates a new solution instance with the document specified updated to be contained in
/// the sequence of logical folders.
/// </summary>
public StateChange WithDocumentFolders(DocumentId documentId, IReadOnlyList<string> folders)
{
var oldDocument = GetRequiredDocumentState(documentId);
if (oldDocument.Folders.SequenceEqual(folders))
{
var oldProject = GetRequiredProjectState(documentId.ProjectId);
return new(this, oldProject, oldProject);
}

return UpdateDocumentState(oldDocument.UpdateFolders(folders), contentChanged: false);
}

/// <summary>
/// Creates a new solution instance with the document specified updated to have the specified file path.
/// </summary>
public StateChange WithDocumentFilePath(DocumentId documentId, string? filePath)
{
var oldDocument = GetRequiredDocumentState(documentId);
if (oldDocument.FilePath == filePath)
var newDocument = oldDocument.WithAttributes(updateAttributes(oldDocument.Attributes, arg));
if (ReferenceEquals(oldDocument, newDocument))
{
var oldProject = GetRequiredProjectState(documentId.ProjectId);
return new(this, oldProject, oldProject);
}

return UpdateDocumentState(oldDocument.UpdateFilePath(filePath), contentChanged: false);
return UpdateDocumentState(newDocument, contentChanged: false);
}

/// <summary>
Expand Down
Loading