// Internal for testing internal void DocumentManager_Changed(object sender, LSPDocumentChangeEventArgs args) { if (args is null) { throw new ArgumentNullException(nameof(args)); } if (args.Kind != LSPDocumentChangeKind.VirtualDocumentChanged) { return; } var lspDocument = args.New; for (var i = 0; i < lspDocument.VirtualDocuments.Count; i++) { if (!_synchronizingContexts.TryGetValue(lspDocument.VirtualDocuments[i].Uri, out var synchronizingContext)) { continue; } if (lspDocument.Version == synchronizingContext.ExpectedHostDocumentVersion) { synchronizingContext.SetSynchronized(true); } else if (lspDocument.Version > synchronizingContext.ExpectedHostDocumentVersion) { // The LSP document version has surpassed what the projected document was expecting for a version. No longer able to synchronize. synchronizingContext.SetSynchronized(false); } } }
// Internal for testing internal void DocumentManager_Changed(object sender, LSPDocumentChangeEventArgs args) { // We need the below check to address a race condition between when a request is sent to the C# server // for a generated document and when the C# server receives a document/didOpen notification. This race // condition may occur when the Razor server finishes initializing before C# receives and processes the // document open request. // This workaround adds the Razor client name to the generated document so the C# server will recognize // it, despite the document not being formally opened. Note this is meant to only be a temporary // workaround until a longer-term solution is implemented in the future. if (args.Kind == LSPDocumentChangeKind.Added && _dynamicFileInfoProvider is DefaultRazorDynamicFileInfoProvider defaultProvider) { defaultProvider.PromoteBackgroundDocument(args.New.Uri, CSharpDocumentPropertiesService.Instance); } if (args.Kind != LSPDocumentChangeKind.VirtualDocumentChanged) { return; } if (args.VirtualNew is CSharpVirtualDocumentSnapshot) { var csharpContainer = new CSharpVirtualDocumentContainer(_lspDocumentMappingProvider, args.New, args.VirtualNew.Snapshot); _dynamicFileInfoProvider.UpdateLSPFileInfo(args.New.Uri, csharpContainer); } }
public async Task TrySynchronizeVirtualDocumentAsync_SimultaneousEqualSynchronizationRequests_ReturnsTrue() { // Arrange var synchronizer = new DefaultLSPDocumentSynchronizer(DocumentManager, JoinableTaskContext); synchronizer._synchronizationTimeout = TimeSpan.FromMilliseconds(500); var originalVirtualDocument = new TestVirtualDocumentSnapshot(VirtualDocumentUri, 123); var originalDocument = new TestLSPDocumentSnapshot(LSPDocumentUri, 124, originalVirtualDocument); // Start synchronize var synchronizeTask1 = synchronizer.TrySynchronizeVirtualDocumentAsync(originalDocument, originalVirtualDocument, CancellationToken.None); var synchronizeTask2 = synchronizer.TrySynchronizeVirtualDocumentAsync(originalDocument, originalVirtualDocument, CancellationToken.None); var newVirtualDocument = originalVirtualDocument.Fork(124); var newDocument = originalDocument.Fork(124, newVirtualDocument); var args = new LSPDocumentChangeEventArgs(originalDocument, newDocument, LSPDocumentChangeKind.VirtualDocumentChanged); // Act synchronizer.DocumentManager_Changed(DocumentManager, args); var result1 = await synchronizeTask1.ConfigureAwait(false); var result2 = await synchronizeTask2.ConfigureAwait(false); // Assert Assert.True(result1); Assert.True(result2); }
public override void UpdateVirtualDocument <TVirtualDocument>( Uri hostDocumentUri, IReadOnlyList <TextChange> changes, long hostDocumentVersion) { if (hostDocumentUri is null) { throw new ArgumentNullException(nameof(hostDocumentUri)); } if (changes is null) { throw new ArgumentNullException(nameof(changes)); } Debug.Assert(_joinableTaskContext.IsOnMainThread); if (!_documents.TryGetValue(hostDocumentUri, out var lspDocument)) { // Don't know about document, noop. return; } var virtualDocumentAcquired = lspDocument.TryGetVirtualDocument <TVirtualDocument>(out var virtualDocument); if (changes.Count == 0 && virtualDocumentAcquired && virtualDocument.HostDocumentSyncVersion == hostDocumentVersion) { // The current virtual document already knows about this update. Ignore it so we don't prematurely invoke a change event. return; } var old = lspDocument.CurrentSnapshot; var oldVirtual = virtualDocument.CurrentSnapshot; var @new = lspDocument.UpdateVirtualDocument <TVirtualDocument>(changes, hostDocumentVersion); if (old == @new) { return; } if (!lspDocument.TryGetVirtualDocument <TVirtualDocument>(out var newVirtualDocument)) { throw new InvalidOperationException("This should never ever happen."); } var newVirtual = newVirtualDocument.CurrentSnapshot; var args = new LSPDocumentChangeEventArgs( old, @new, oldVirtual, newVirtual, LSPDocumentChangeKind.VirtualDocumentChanged); Changed?.Invoke(this, args); }
public void DocumentManager_Changed_Removed_Noops() { // Arrange var fileInfoProvider = new Mock <RazorDynamicFileInfoProvider>(MockBehavior.Strict); var publisher = new CSharpVirtualDocumentPublisher(fileInfoProvider.Object); var args = new LSPDocumentChangeEventArgs(old: Mock.Of <LSPDocumentSnapshot>(), @new: null, LSPDocumentChangeKind.Removed); // Act & Assert publisher.DocumentManager_Changed(sender: null, args); }
// Internal for testing internal void DocumentManager_Changed(object sender, LSPDocumentChangeEventArgs args) { if (args.Kind != LSPDocumentChangeKind.VirtualDocumentChanged) { return; } if (args.VirtualNew is CSharpVirtualDocumentSnapshot) { var csharpContainer = new CSharpVirtualDocumentContainer(args.VirtualNew.Snapshot); _dynamicFileInfoProvider.UpdateLSPFileInfo(args.New.Uri, csharpContainer); } }
public void DocumentManager_Changed_VirtualDocumentChanged_UpdatesFileInfo() { // Arrange var csharpSnapshot = new CSharpVirtualDocumentSnapshot(new Uri("C:/path/to/something.razor.g.cs"), Mock.Of <ITextSnapshot>(), hostDocumentSyncVersion: 1337); var lspDocument = new TestLSPDocumentSnapshot(new Uri("C:/path/to/something.razor"), 1337, csharpSnapshot); var fileInfoProvider = new Mock <RazorDynamicFileInfoProvider>(MockBehavior.Strict); fileInfoProvider.Setup(provider => provider.UpdateLSPFileInfo(lspDocument.Uri, It.IsAny <DynamicDocumentContainer>())) .Verifiable(); var publisher = new CSharpVirtualDocumentPublisher(fileInfoProvider.Object); var args = new LSPDocumentChangeEventArgs( old: Mock.Of <LSPDocumentSnapshot>(), @new: lspDocument, virtualOld: Mock.Of <VirtualDocumentSnapshot>(), virtualNew: csharpSnapshot, LSPDocumentChangeKind.VirtualDocumentChanged); // Act publisher.DocumentManager_Changed(sender: null, args); // Assert fileInfoProvider.VerifyAll(); }
// Internal for testing internal void DocumentManager_Changed(object sender, LSPDocumentChangeEventArgs args) { if (args is null) { throw new ArgumentNullException(nameof(args)); } lock (DocumentContextLock) { if (args.Kind == LSPDocumentChangeKind.Added) { var lspDocument = args.New; for (var i = 0; i < lspDocument.VirtualDocuments.Count; i++) { var virtualDocument = lspDocument.VirtualDocuments[i]; Debug.Assert(!_virtualDocumentContexts.ContainsKey(virtualDocument.Uri)); var virtualDocumentTextBuffer = virtualDocument.Snapshot.TextBuffer; virtualDocumentTextBuffer.PostChanged += VirtualDocumentBuffer_PostChanged; _virtualDocumentContexts[virtualDocument.Uri] = new DocumentContext(_synchronizationTimeout); } } else if (args.Kind == LSPDocumentChangeKind.Removed) { var lspDocument = args.Old; for (var i = 0; i < lspDocument.VirtualDocuments.Count; i++) { var virtualDocument = lspDocument.VirtualDocuments[i]; Debug.Assert(_virtualDocumentContexts.ContainsKey(virtualDocument.Uri)); var virtualDocumentTextBuffer = virtualDocument.Snapshot.TextBuffer; virtualDocumentTextBuffer.PostChanged -= VirtualDocumentBuffer_PostChanged; _virtualDocumentContexts.Remove(virtualDocument.Uri); } } } }
public override void UntrackDocument(ITextBuffer buffer) { if (buffer is null) { throw new ArgumentNullException(nameof(buffer)); } Debug.Assert(_joinableTaskContext.IsOnMainThread); var uri = _fileUriProvider.GetOrCreate(buffer); if (!_documents.TryGetValue(uri, out var lspDocument)) { // We don't know about this document, noop. return; } _documents.Remove(uri); var args = new LSPDocumentChangeEventArgs(lspDocument.CurrentSnapshot, @new: null, LSPDocumentChangeKind.Removed); Changed?.Invoke(this, args); }
public override void TrackDocument(ITextBuffer buffer) { if (buffer is null) { throw new ArgumentNullException(nameof(buffer)); } Debug.Assert(_joinableTaskContext.IsOnMainThread); var uri = _fileUriProvider.GetOrCreate(buffer); if (_documents.TryGetValue(uri, out _)) { throw new InvalidOperationException($"Can not track document that's already being tracked {uri}"); } var lspDocument = _documentFactory.Create(buffer); _documents[uri] = lspDocument; var args = new LSPDocumentChangeEventArgs(old: null, lspDocument.CurrentSnapshot, LSPDocumentChangeKind.Added); Changed?.Invoke(this, args); }
public async Task TrySynchronizeVirtualDocumentAsync_SynchronizesAfterUpdate_ReturnsTrue() { // Arrange var synchronizer = new DefaultLSPDocumentSynchronizer(DocumentManager, JoinableTaskContext); synchronizer._synchronizationTimeout = TimeSpan.FromMilliseconds(500); var originalVirtualDocument = new TestVirtualDocumentSnapshot(VirtualDocumentUri, 123); var originalDocument = new TestLSPDocumentSnapshot(LSPDocumentUri, 124, originalVirtualDocument); // Start synchronization, this will hang until we invoke a DocumentManager_Changed event because the above virtual document expects host doc version 123 but the host doc is 124 var synchronizeTask = synchronizer.TrySynchronizeVirtualDocumentAsync(originalDocument, originalVirtualDocument, CancellationToken.None); // Create a virtual and host doc that are synchronized (both at version 124). var newVirtualDocument = originalVirtualDocument.Fork(124); var newDocument = originalDocument.Fork(124, newVirtualDocument); var args = new LSPDocumentChangeEventArgs(originalDocument, newDocument, LSPDocumentChangeKind.VirtualDocumentChanged); // Act synchronizer.DocumentManager_Changed(DocumentManager, args); var result = await synchronizeTask.ConfigureAwait(false); // Assert Assert.True(result); }
public async Task TrySynchronizeVirtualDocumentAsync_SimultaneousDifferentSynchronizationRequests_CancelsFirst_ReturnsFalseThenTrue() { // Arrange var synchronizer = new DefaultLSPDocumentSynchronizer(DocumentManager, JoinableTaskContext); synchronizer._synchronizationTimeout = TimeSpan.FromMilliseconds(500); var originalVirtualDocument = new TestVirtualDocumentSnapshot(VirtualDocumentUri, 123); var originalDocument = new TestLSPDocumentSnapshot(LSPDocumentUri, 124, originalVirtualDocument); // Start synchronization that will hang because 123 != 124 var synchronizeTask1 = synchronizer.TrySynchronizeVirtualDocumentAsync(originalDocument, originalVirtualDocument, CancellationToken.None); var newVirtualDocument = originalVirtualDocument.Fork(124); var newDocument = originalDocument.Fork(125, newVirtualDocument); // Start another synchronization that will also hang because 124 != 125. However, this synchronization request is for the same addressable virtual document (same URI) // therefore requesting a second synchronization with a different host doc version expectation will cancel the original synchronization request resulting it returning // false. var synchronizeTask2 = synchronizer.TrySynchronizeVirtualDocumentAsync(newDocument, newVirtualDocument, CancellationToken.None); var finalVirtualDocument = newVirtualDocument.Fork(125); var finalDocument = newDocument.Fork(125, finalVirtualDocument); var args = new LSPDocumentChangeEventArgs(newDocument, finalDocument, LSPDocumentChangeKind.VirtualDocumentChanged); // Act synchronizer.DocumentManager_Changed(DocumentManager, args); var result1 = await synchronizeTask1.ConfigureAwait(false); var result2 = await synchronizeTask2.ConfigureAwait(false); // Assert Assert.False(result1); Assert.True(result2); }
private static void NotifyLSPDocumentAdded(LSPDocumentSnapshot lspDocument, DefaultLSPDocumentSynchronizer synchronizer) { var args = new LSPDocumentChangeEventArgs(old: null, @new: lspDocument, LSPDocumentChangeKind.Added); synchronizer.DocumentManager_Changed(sender: null, args); }