diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index e4b128860bafd..b0d05af388885 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -708,20 +708,13 @@ public SolutionCompilationState WithDocumentFilePath( this.SolutionState.WithDocumentFilePath(documentId, filePath), documentId); } - /// - public SolutionCompilationState WithDocumentText(DocumentId documentId, SourceText text, PreservationMode mode) - => WithDocumentTexts([(documentId, text, mode)]); - - internal SolutionCompilationState WithDocumentTexts( - ImmutableArray<(DocumentId documentId, SourceText text, PreservationMode mode)> texts) - { - return WithDocumentContents( - texts, IsUnchanged, + internal SolutionCompilationState WithDocumentTexts(ImmutableArray<(DocumentId documentId, SourceText text, PreservationMode mode)> texts) + => WithDocumentContents( + texts, SourceTextIsUnchanged, static (documentState, text, mode) => documentState.UpdateText(text, mode)); - static bool IsUnchanged(DocumentState oldDocument, SourceText text) - => oldDocument.TryGetText(out var oldText) && text == oldText; - } + private static bool SourceTextIsUnchanged(DocumentState oldDocument, SourceText text) + => oldDocument.TryGetText(out var oldText) && text == oldText; private SolutionCompilationState WithDocumentContents( ImmutableArray<(DocumentId documentId, TContent content, PreservationMode mode)> texts, @@ -1658,7 +1651,7 @@ public SolutionCompilationState WithCachedSourceGeneratorState(ProjectId project /// public SolutionCompilationState WithDocumentText(IEnumerable documentIds, SourceText text, PreservationMode mode) { - var result = this; + using var _ = ArrayBuilder<(DocumentId, SourceText, PreservationMode)>.GetInstance(out var changedDocuments); foreach (var documentId in documentIds) { @@ -1670,10 +1663,22 @@ public SolutionCompilationState WithDocumentText(IEnumerable docume var documentState = this.SolutionState.GetProjectState(documentId.ProjectId)?.DocumentStates.GetState(documentId); if (documentState != null) - result = result.WithDocumentText(documentId, text, mode); + { + // before allocating an array below (and calling into a function that does a fair amount of linq work), + // do a fast check if the text has actually changed. this shows up in allocation traces and is + // worthwhile to avoid for the common case where we're continually being asked to update the same doc to + // the same text (for example, when GetOpenDocumentInCurrentContextWithChanges) is called. + // + // The use of GetRequiredState mirrors what happens in WithDocumentTexts + if (!SourceTextIsUnchanged(documentState, text)) + changedDocuments.Add((documentId, text, mode)); + } } - return result; + if (changedDocuments.Count == 0) + return this; + + return this.WithDocumentTexts(changedDocuments.ToImmutableAndClear()); } internal TestAccessor GetTestAccessor()