diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index fea47f1ac5bee..6f1b14d5dc446 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -221,7 +221,7 @@ private async ValueTask SynchronizeTextChangesAsync( // // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves // times we need to do full text synchronization for typing scenario. - using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes)>.GetInstance(out var builder); + using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes, Checksum newTextChecksum)>.GetInstance(out var builder); foreach (var (oldDocument, newDocument) in values) { @@ -242,9 +242,10 @@ private async ValueTask SynchronizeTextChangesAsync( continue; var state = await oldDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var newState = await newDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - builder.Add((oldDocument.Id, state.Text, textChanges)); + builder.Add((oldDocument.Id, state.Text, textChanges, newState.Text)); } if (builder.Count == 0) diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index fb57ce4999708..fd9622f9ae376 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -89,15 +89,15 @@ public async Task TestRemoteHostTextSynchronize() // update text var newText = oldText.WithChanges(new TextChange(TextSpan.FromBounds(0, 0), "/* test */")); - // sync - await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable())], cancellationToken), - CancellationToken.None); - // apply change to solution var newDocument = oldDocument.WithText(newText); var newState = await newDocument.State.GetStateChecksumsAsync(CancellationToken.None); + // sync + await client.TryInvokeAsync( + (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), newState.Text)], cancellationToken), + CancellationToken.None); + // check that text already exist in remote side Assert.True(client.TestData.WorkspaceManager.SolutionAssetCache.TryGetAsset(newState.Text, out var serializableRemoteText)); Assert.Equal(newText.ToString(), (await serializableRemoteText.GetTextAsync(CancellationToken.None)).ToString()); diff --git a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs index 06e2b900e3035..b3f854ae1a892 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs @@ -57,26 +57,31 @@ internal sealed class SerializableSourceText public readonly Checksum ContentChecksum; public SerializableSourceText(TemporaryStorageTextHandle storageHandle) - : this(storageHandle, text: null, storageHandle.ContentHash) + : this(storageHandle, text: null, Checksum.Create(storageHandle.ContentHash)) { } public SerializableSourceText(SourceText text, ImmutableArray contentHash) - : this(storageHandle: null, text, contentHash) + : this(storageHandle: null, text, Checksum.Create(contentHash)) { } - private SerializableSourceText(TemporaryStorageTextHandle? storageHandle, SourceText? text, ImmutableArray contentHash) + public SerializableSourceText(SourceText text, Checksum contentChecksum) + : this(storageHandle: null, text, contentChecksum) + { + } + + private SerializableSourceText(TemporaryStorageTextHandle? storageHandle, SourceText? text, Checksum contentChecksum) { Debug.Assert(storageHandle is null != text is null); _storageHandle = storageHandle; _text = text; - ContentChecksum = Checksum.Create(contentHash); + ContentChecksum = contentChecksum; #if DEBUG var computedContentHash = TryGetText()?.GetContentHash() ?? _storageHandle!.ContentHash; - Debug.Assert(contentHash.SequenceEqual(computedContentHash)); + Debug.Assert(contentChecksum == Checksum.Create(computedContentHash)); #endif } diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index 03e5d8e76a289..079bc05f93d52 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -26,7 +26,7 @@ internal interface IRemoteAssetSynchronizationService /// entire contents of the file over. /// ValueTask SynchronizeTextChangesAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)> changes, + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, CancellationToken cancellationToken); /// diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index 8bec38df958f1..f6cc7c147aab3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -49,7 +49,7 @@ public ValueTask SynchronizeActiveDocumentAsync(DocumentId? documentId, Cancella } public ValueTask SynchronizeTextChangesAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)> changes, + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => @@ -58,7 +58,7 @@ public ValueTask SynchronizeTextChangesAsync( using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, cancellationToken)) { - foreach (var (documentId, baseTextChecksum, textChanges) in changes) + foreach (var (documentId, baseTextChecksum, textChanges, newTextChecksum) in changes) { // Try to get the text associated with baseTextChecksum var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); @@ -73,7 +73,7 @@ public ValueTask SynchronizeTextChangesAsync( // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over // the entire document. var newText = text.WithChanges(textChanges); - var newSerializableText = new SerializableSourceText(newText, newText.GetContentHash()); + var newSerializableText = new SerializableSourceText(newText, newTextChecksum); WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); }